email = $email; $this->password = $password; //assign if ($autoConnect) { $this->_connected = $this->connect($email, $password, $this->server, $this->port); //try to connect } } /** * check whether the login attempt was succesful * * @return bool */ public function loggedIn() { return $this->_connected; } /** * get the contact list * * @return Naneau_MSN_Messenger_ContactList */ public function getContactList() { if (!$this->_connected) { throw new Exception('Not connected, can\'t retrieve contact list'); } if ($this->_contactList == null) { $this->buildContactList(); } return $this->_contactList; //new contact list } /** * set status * * @param int $status * @return void */ public function setStatus($status) { if ($this->_contactList == null) { $this->getContactList(); } //contact list must have been retrieved $this->sendCommand('CHG', $status); } /** * connect to the server * * @return bool * @throws Exception if it can't open a connection */ private function connect() { $this->getSock($this->server, $this->port); //create connection $this->sendCommand('VER', 'MSNP9 CVR0'); //start login while ($response = $this->getResponse()) { //login chain switch (substr($response, 0, 3)) { case 'USR': //user related command $data = explode(' ', trim($response)); //split the data if ($data[2] == 'TWN') { //challenge (not logged in yet) if ($ticket = $this->passportLogin($data[4], $this->email, $this->password)) { //try to do 'real' login with passport $this->sendCommand('USR', 'TWN S ' . $ticket); //replay with ticket } else { //passport login failed return false; } } else { //succesful login! $this->fetchData(); //get data return true; } break; case 'VER': //server returns version $this->sendCommand('CVR', '0x0409 win 4.10 i386 MSNMSGR 7.0.0816 MSMSGS ' . $this->email); //reply with our 'version' and email break; case 'CVR': //protocol information $this->sendCommand('USR', 'TWN I ' . $this->email); //try to login break; case 'XFR': //redirect $server = explode(' ', $response); $server = explode(':', $server[3]); //split into server and port $this->getSock($server[0], intval($server[1])); //reconnect $this->sendCommand('VER', 'MSNP9 CVR0'); //restart with the version break; default: //unknown command //we can't have those! //really, we can't :x return false; break; } } } /** * receive contact list info * */ private function buildContactList() { $contacts = array(); //the array of contacts $this->sendCommand('SYN', 0); //start synchronisation of contact list (pretend it's never happend) while ($response = $this->getResponse()) { if (substr($response, 0, 3) == 'LST') { $data = explode(' ', $response); $contacts[] = new Naneau_MSN_Messenger_Contact($data[1], urldecode($data[2])); } } $this->_contactList = new Naneau_MSN_Messenger_ContactList($contacts); } /** * fetch extra data after login * * @return void */ private function fetchData() { while ($response = $this->getResponse()) { $data = explode(':', $response); if (isset($data[1])) { $this->_data[$data[0]] = trim($data[1]); //save data } } } /** * create a socket * * @param string $server * @param int $port */ private function getSock($server, $port) { $this->_msgCount = 1; //reset msgCount if ($this->_con) { fclose($this->_con); } //if connection, reset it if (!($this->_con = @fsockopen($server, $port, $errno, $errstr, 2))) { //can't connect throw new Exception('Can\'t open connection socket: ' . $server . ':' . $port); } } /** * do the passport login * * @param string $server * @param int $port */ private function passportLogin($challenge) { $http = new Zend_Http_Client($this->passport); $response = $http->request(); //do request to passport $headers = $response->getHeaders(); //get response headers preg_match ('/DALogin=(.*?),/', $headers['Passporturls'], $matches); if (isset($matches[1])) { //if the first parenthesized part (the real url) exists $url = $matches[1]; //the login url $http = new Zend_Http_Client('https://' . $matches[1]); $http->setHeaders('Authorization', 'Passport1.4 OrgVerb=GET,OrgURL=http%3A%2F%2Fmessenger%2Emsn%2Ecom,sign-in=' . $this->email . ',pwd=' . $this->password . ',' . $challenge . '"'); $response = $http->request(); $headers = $response->getHeaders(); //send login request to 'real' server we received in first request preg_match ('/from-PP=\'(.*?)\'/', $headers['Authentication-info'], $matches); //try to get a ticket out of the response headers return isset($matches[1]) ? $matches[1] : false; //must get a ticket, or it failed } else { //something went wrong, no url in the response return false; } } /** * get a response from the server, or false if there is none * * @return string|bool $response */ public function getResponse() { if (!feof($this->_con) && $response = @fgets($this->_con)) { //not end of file for connection, and there is a response if ($this->_debug) { echo '<<< ' . $response . '
'; } return $response; } return false; } /** * write a command the socket * * @param string $command * @param string $options * @return void */ public function sendCommand($command, $options) { $dString = $command . ' ' . $this->_msgCount++ . ' ' . $options . "\r\n"; //the command if ($this->_debug) { echo '>>> ' . $dString . '
'; } fwrite($this->_con, $dString); //output it } }