<?php
//Copyright Atmail

class Atmail_Provider_SocialNetwork_FacebookSimpleChat
{
	
	private $fp = null;
	private $app_id = null;
	private $uid = null;
	private $access_token = null;
	private $xmpp_server = 'chat.facebook.com';
	private $lastResponseXMLChunk = null;
	private $lastXMLResponseChunkArray = array("val" => null,"index" => null);
	private $debug = false;
	
	public function __construct( Array $configArray )
	{
		
		if( !array_key_exists('app_id', $configArray) )
			throw new Exception(__METHOD__ . ' #' . __LINE__ . ": app_id missing");
		$this->app_id = $configArray['app_id'];
		
		if( !array_key_exists('uid', $configArray) )
			throw new Exception(__METHOD__ . ' #' . __LINE__ . ": uid missing");
		$this->uid = $configArray['uid'];
		
		if( !array_key_exists('access_token', $configArray) )
			throw new Exception(__METHOD__ . ' #' . __LINE__ . ": access_token missing");
		$this->access_token = $configArray['access_token'];
		
		if( array_key_exists('debug', $configArray) )
			$this->debug = $configArray['debug'];
		
	}

	public function open_connection() 
	{
	
		$this->fp = fsockopen($this->xmpp_server, 5222, $this->errNo, $this->errStr);
		if( !$this->fp )
		{
		
			if( $this->debug ) Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ":XMPP FAILURE:" . $this->errNo . ": " . $this->errStr);
			if( $this->debug ) Zend_Registry::get('log')->debug( "\n" . print_r($this->lastResponseXMLChunk, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$this->lastResponseXMLChunk \n");
			return false;
		
		}
		else
			return true;
	
	}
	
	public function prepareAuthenticatedSession($quick = false, $to = null, $message = null) 
	{

	
		$STREAM_XML = '<stream:stream xmlns:stream="http:/'.'/etherx.jabber.org/streams" version="1.0" xmlns="jabber:client" to="chat.facebook.com" xml:lang="en" xmlns:xml="http:/'.'/www.w3.org/XML/1998/namespace">';
		$this->send_xml($STREAM_XML);
		if( !$this->find_xmpp('STREAM:STREAM') ) 
		{
	
			$this->errStr = "Failed initialising stream: no 'STREAM:STREAM' found";
			$this->errNo = 1;
			if( $this->debug ) Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ":XMPP FAILURE:" . $this->errNo . ": " . $this->errStr);
			if( $this->debug ) Zend_Registry::get('log')->debug( "\n" . print_r($this->lastResponseXMLChunk, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$this->lastResponseXMLChunk \n");
			return false;
	
		}
		
		if( !$this->find_xmpp('MECHANISM', 'X-FACEBOOK-PLATFORM')) 
		{
			$this->errStr = "Failed: 'MECHANISM' = 'X-FACEBOOK-PLATFORM' not found.";
			$this->errNo = 2;
			if( $this->debug ) Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ":XMPP FAILURE:" . $this->errNo . ": " . $this->errStr);
			if( $this->debug ) Zend_Registry::get('log')->debug( "\n" . print_r($this->lastResponseXMLChunk, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$this->lastResponseXMLChunk \n");
			return false;
		}
		
		$START_TLS = '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>';
		$this->send_xml($START_TLS);
		if( !$this->find_xmpp('PROCEED', null, $proceed)) 
		{
			$this->errStr = "Failed issuing start TLS did not result in PROCEED response.";
			$this->errNo = 4;
			if( $this->debug ) Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ":XMPP FAILURE:" . $this->errNo . ": " . $this->errStr);
			if( $this->debug ) Zend_Registry::get('log')->debug( "\n" . print_r($this->lastResponseXMLChunk, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$this->lastResponseXMLChunk \n");
			return false;
		}
		stream_socket_enable_crypto($this->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);

		$this->send_xml($STREAM_XML);
		if( !$this->find_xmpp('STREAM:STREAM') ) 
		{
			$this->errStr = "Failed starting stream.";
			$this->errNo = 8;
			if( $this->debug ) Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ":XMPP FAILURE:" . $this->errNo . ": " . $this->errStr);
			if( $this->debug ) Zend_Registry::get('log')->debug( "\n" . print_r($this->lastResponseXMLChunk, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$this->lastResponseXMLChunk \n");
			return false;
		}
		if( !$this->find_xmpp('MECHANISM', 'X-FACEBOOK-PLATFORM') ) 
		{
			$this->errStr = "Failed: 'MECHANISM' = 'X-FACEBOOK-PLATFORM' not found.";
			$this->errNo = 16;
			if( $this->debug ) Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ":XMPP FAILURE:" . $this->errNo . ": " . $this->errStr);
			if( $this->debug ) Zend_Registry::get('log')->debug( "\n" . print_r($this->lastResponseXMLChunk, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$this->lastResponseXMLChunk \n");
			return false;
		}
		
		// gets challenge from server and decode it
		$AUTH_XML = '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="X-FACEBOOK-PLATFORM"></auth>';
		$this->send_xml($AUTH_XML);
		if( !$this->find_xmpp('CHALLENGE', null, $challenge) ) 
		{
			$this->errStr = "Failed: 'CHALLENGE' not found.";
			$this->errNo = 32;
			if( $this->debug ) Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ":XMPP FAILURE:" . $this->errNo . ": " . $this->errStr);
			if( $this->debug ) Zend_Registry::get('log')->debug( "\n" . print_r($this->lastResponseXMLChunk, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$this->lastResponseXMLChunk \n");
			return false;
		}
		$challenge = base64_decode($challenge);
		$challenge = urldecode($challenge);
		parse_str($challenge, $challenge_array);

		// creates the response array
		$resp_array = array(
			'method' => $challenge_array['method'],
			'nonce' => $challenge_array['nonce'],
			'access_token' => $this->access_token,
			'api_key' => $this->app_id,
			'call_id' => 0,
			'v' => '1.0',
			);
		// creates signature
		$response = http_build_query($resp_array);

		// sends the response and waits for success
		$xml = '<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">' . base64_encode($response) . '</response>';
		$this->send_xml( $xml);
		if( !$this->find_xmpp( 'SUCCESS') ) 
		{
			return false; //if sending multiple messages (e.g. campaign FB invite) then get new nonce from each recponse
		}

		// finishes auth process
		$this->send_xml( $STREAM_XML);
		if( !$this->find_xmpp('STREAM:STREAM')) 
		{
			return false;
		}
		if( !$this->find_xmpp( 'STREAM:FEATURES')) 
		{
			return false;
		}

		$RESOURCE_XML = '<iq type="set" id="3"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>fb_xmpp_script</resource></bind></iq>';
		$this->send_xml( $RESOURCE_XML);
		if( !$this->find_xmpp( 'JID') ) 
		{
			return false;
		}

		$SESSION_XML = '<iq type="set" id="4" to="chat.facebook.com"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>';
		$this->send_xml( $SESSION_XML);
		if( !$this->find_xmpp( 'SESSION')) 
		{
			return false;
		}
		
		return true;

	}

	public function sendMessage($to, $message)
	{
	
	
		$MESSAGE_XML = '<message to="-' . $to . '@chat.facebook.com" id="AtmailMessage' . rand(100000,999999) . '"><body>' . $message . '</body></message>';
		$this->send_xml($MESSAGE_XML);
		$result = $this->recv_xml();
		if( $this->debug ) Zend_Registry::get('log')->debug( "\n" . print_r($result, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$result \n");
		
		//echo print_r($result, true);
		//$result = fread($fp, 4096);
		//echo htmlentities($result,null,'utf-8');
		return true;
		
	}
	
	public function closeConnection()
	{
	
		$CLOSE_XML = '</stream:stream>';
		$this->send_xml( $CLOSE_XML);
	
		fclose($this->fp);

		return true;
	
	}


/*
//Gets access_token with xmpp_login permission
function get_access_token($app_id, $app_secret, $my_url){ 

	$code = $_REQUEST["code"];

	if( empty($code) || $code == '' || $code == null ) 
	{
		$dialog_url = "http://www.facebook.com/dialog/oauth?scope=xmpp_login".
		"&client_id=" . $app_id . "&redirect_uri=" . urlencode($my_url) ;
		echo("<script>top.location.href='" . $dialog_url . "'</script>");
	}
	else
	{
		$token_url = "https://graph.facebook.com/oauth/access_token?client_id="
		. $app_id . "&redirect_uri=" . urlencode($my_url) 
			. "&client_secret=" . $app_secret 
			. "&code=" . $code;
		echo $token_url;
		exit;   
		$access_token = file_get_contents($token_url);
		parse_str($access_token, $output);

		return($output['access_token']);
	}
}
*/



	private function send_xml($xml) 
	{

		fwrite($this->fp, $xml);

	}

	private function recv_xml($size=4096) 
	{

		$this->lastResponseXMLChunk = fread($this->fp, $size);
		if( $this->debug ) Zend_Registry::get('log')->debug( "\n" . print_r($this->lastResponseXMLChunk, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$this->lastResponseXMLChunk \n");
		
		if( $this->lastResponseXMLChunk === "" )
			$this->lastXMLResponseChunkArray = array("val" => null,"index" => null);

		$xml_parser = xml_parser_create();
		xml_parse_into_struct($xml_parser, $this->lastResponseXMLChunk, $val, $index);
		xml_parser_free($xml_parser);
		$this->lastXMLResponseChunkArray = array("val" => $val, "index" => $index);
		//Zend_Registry::get('log')->debug( "\n" . print_r($this->lastXMLResponseChunkArray, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$this->lastXMLResponseChunkArray \n");
		
		return $this->lastXMLResponseChunkArray;

	}

	private function find_xmpp( $tag, $value=null, &$ret=null) 
	{

		do 
		{
			if( $this->lastXMLResponseChunkArray['val'] === null && $this->lastXMLResponseChunkArray['index'] === null ) 
			{
			
				$this->recv_xml();
				if( $this->lastXMLResponseChunkArray['val'] === null && $this->lastXMLResponseChunkArray['index'] === null ) 
					return false;
			
			}
			//parse chinks of the response looking for tag and or value while not eof (respone)
			foreach( $this->lastXMLResponseChunkArray['index'] as $tag_key => $tag_array )
			{
			
				if( $tag_key === $tag )
				{
				
					if( $value === null )
					{
					
						if( isset($this->lastXMLResponseChunkArray['val'][$tag_array[0]]['value'])) 
							$ret = $this->lastXMLResponseChunkArray['val'][$tag_array[0]]['value'];
						return true;
				
					}
					foreach ($tag_array as $i => $pos) 
					{
					
						if( $this->lastXMLResponseChunkArray['val'][$pos]['tag'] === $tag && isset($this->lastXMLResponseChunkArray['val'][$pos]['value']) && $this->lastXMLResponseChunkArray['val'][$pos]['value'] === $value) 
						{
					
							$ret = $this->lastXMLResponseChunkArray['val'][$pos]['value'];
							return true;
					
						}
				
					}
			
				}
		
			}
			$ret = $this->lastXMLResponseChunkArray['val'] = $this->lastXMLResponseChunkArray['index'] = null;
		} 
		while( !feof($this->fp)) ;
		return false;

	}

}