- 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
- $aContactList = $this->getMembershipList();\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
- $this->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 ($this->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
- $this->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
- $this->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
- $this->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 ($this->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
- $this->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