0

我想通过 Facebook Chat API (XMPP) 连接,使用 Facebook PHP API 获取 access_token。我正在使用来自官方网页的代码(但包含在课堂上),但它对我不起作用 - 我被困在挑战握手中。

通讯流程:

[INFO] Opening connection... connnection open

<?xml version='1.0' ?><stream:stream from='chat.facebook.com' id='1' version='1.0' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' xml:lang='en'><stream:features><starttls xmlns='urn:ietf:param
s:xml:ns:xmpp-tls'/><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>X-FACEBOOK-PLATFORM</mechanism><mechanism>PLAIN</mechanism></mechanisms></stream:features>
Array
(
    [0] => Array
        (
            [tag] => STREAM:STREAM
            [type] => open
            [level] => 1
            [attributes] => Array
                (
                    [FROM] => chat.facebook.com
                    [ID] => 1
                    [VERSION] => 1.0
                    [XMLNS:STREAM] => http://etherx.jabber.org/streams
                    [XMLNS] => jabber:client
                    [XML:LANG] => en
                )

        )

    [1] => Array
        (
            [tag] => STREAM:FEATURES
            [type] => open
            [level] => 2
        )

    [2] => Array
        (
            [tag] => STARTTLS
            [type] => complete
            [level] => 3
            [attributes] => Array
                (
                    [XMLNS] => urn:ietf:params:xml:ns:xmpp-tls
                )

        )

    [3] => Array
        (
            [tag] => MECHANISMS
            [type] => open
            [level] => 3
            [attributes] => Array
                (
                    [XMLNS] => urn:ietf:params:xml:ns:xmpp-sasl
                )

        )

    [4] => Array
        (
            [tag] => MECHANISM
            [type] => complete
            [level] => 4
            [value] => X-FACEBOOK-PLATFORM
        )

    [5] => Array
        (
            [tag] => MECHANISM
            [type] => complete
            [level] => 4
            [value] => PLAIN
        )

    [6] => Array
        (
            [tag] => MECHANISMS
            [type] => close
            [level] => 3
        )

    [7] => Array
        (
            [tag] => STREAM:FEATURES
            [type] => close
            [level] => 2
        )

)
Array
(
    [STREAM:STREAM] => Array
        (
            [0] => 0
        )

    [STREAM:FEATURES] => Array
        (
            [0] => 1
            [1] => 7
        )

    [STARTTLS] => Array
        (
            [0] => 2
        )

    [MECHANISMS] => Array
        (
            [0] => 3
            [1] => 6
        )

    [MECHANISM] => Array
        (
            [0] => 4
            [1] => 5
        )

)

<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
Array
(
    [0] => Array
        (
            [tag] => PROCEED
            [type] => complete
            [level] => 1
            [attributes] => Array
                (
                    [XMLNS] => urn:ietf:params:xml:ns:xmpp-tls
                )

        )

)
Array
(
    [PROCEED] => Array
        (
            [0] => 0
        )

)

<?xml version='1.0' ?><stream:stream from='chat.facebook.com' id='1' version='1.0' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' xml:lang='en'><stream:features><mechanisms xmlns='urn:ietf:par
ams:xml:ns:xmpp-sasl'><mechanism>X-FACEBOOK-PLATFORM</mechanism><mechanism>PLAIN</mechanism></mechanisms></stream:features>
Array
(
    [0] => Array
        (
            [tag] => STREAM:STREAM
            [type] => open
            [level] => 1
            [attributes] => Array
                (
                    [FROM] => chat.facebook.com
                    [ID] => 1
                    [VERSION] => 1.0
                    [XMLNS:STREAM] => http://etherx.jabber.org/streams
                    [XMLNS] => jabber:client
                    [XML:LANG] => en
                )

        )

    [1] => Array
        (
            [tag] => STREAM:FEATURES
            [type] => open
            [level] => 2
        )

    [2] => Array
        (
            [tag] => MECHANISMS
            [type] => open
            [level] => 3
            [attributes] => Array
                (
                    [XMLNS] => urn:ietf:params:xml:ns:xmpp-sasl
                )

        )

    [3] => Array
        (
            [tag] => MECHANISM
            [type] => complete
            [level] => 4
            [value] => X-FACEBOOK-PLATFORM
        )

    [4] => Array
        (
            [tag] => MECHANISM
            [type] => complete
            [level] => 4
            [value] => PLAIN
        )

    [5] => Array
        (
            [tag] => MECHANISMS
            [type] => close
            [level] => 3
        )

    [6] => Array
        (
            [tag] => STREAM:FEATURES
            [type] => close
            [level] => 2
        )

)
Array
(
    [STREAM:STREAM] => Array
        (
            [0] => 0
        )

    [STREAM:FEATURES] => Array
        (
            [0] => 1
            [1] => 6
        )

    [MECHANISMS] => Array
        (
            [0] => 2
            [1] => 5
        )

    [MECHANISM] => Array
        (
            [0] => 3
            [1] => 4
        )

)
 sent
<
Array
(
)
Array
(
)

<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>dmVyc2lvbj0xJm1ldGhvZD1hdXRoLnhtcHBfbG9naW4mbm9uY2U9OTY4Qzc3MjU1NzE5RDQzQkM3QkNDNDIxQTRGRUNCMDc=</challenge>
Array
(
    [0] => Array
        (
            [tag] => CHALLENGE
            [type] => complete
            [level] => 1
            [attributes] => Array
                (
                    [XMLNS] => urn:ietf:params:xml:ns:xmpp-sasl
                )

            [value] => dmVyc2lvbj0xJm1ldGhvZD1hdXRoLnhtcHBfbG9naW4mbm9uY2U9OTY4Qzc3MjU1NzE5RDQzQkM3QkNDNDIxQTRGRUNCMDc=
        )

)
Array
(
    [CHALLENGE] => Array
        (
            [0] => 0
        )

)
C: dmVyc2lvbj0xJm1ldGhvZD1hdXRoLnhtcHBfbG9naW4mbm9uY2U9OTY4Qzc3MjU1NzE5RDQzQkM3QkNDNDIxQTRGRUNCMDc=

<
Array
(
)
Array
(
)

<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure></stream:stream>
Array
(
    [0] => Array
        (
            [tag] => FAILURE
            [type] => open
            [level] => 1
            [attributes] => Array
                (
                    [XMLNS] => urn:ietf:params:xml:ns:xmpp-sasl
                )

        )

    [1] => Array
        (
            [tag] => NOT-AUTHORIZED
            [type] => complete
            [level] => 2
        )

    [2] => Array
        (
            [tag] => FAILURE
            [type] => close
            [level] => 1
        )

)
Array
(
    [FAILURE] => Array
        (
            [0] => 0
            [1] => 2
        )

    [NOT-AUTHORIZED] => Array
        (
            [0] => 1
        )

)
An error ocurred
Authentication complete

和我的代码(FB_API 是包装标准 facebook api 的类的实例,它只使用一次来获取访问令牌,将 Utility_Facebook_Chat::$debug 设置为 true 以查看所有消息):

<?php

class Utility_Facebook_Chat {

    private $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">';
    private $AUTH_XML = '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="X-FACEBOOK-PLATFORM"></auth>';
    private $CLOSE_XML = '</stream:stream>';
    private $RESOURCE_XML = '<iq type="set" id="3"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>fb_xmpp_script</resource></bind></iq>';
    private $SESSION_XML = '<iq type="set" id="4" to="chat.facebook.com"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>';
    private $START_TLS = '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>';
    private $MESSAGE_XML = '<message from="-%d@chat.facebook.com" to="-%d@chat.facebook.com"><body>%s</body></message>';

    public static $debug = false;

    private $fsocket;
    private $FB_API;

    private $FB_USER_ID;

    const CHAT_SERVER = 'chat.facebook.com';

    public static $instances = array();

    public static function getInstance($fbUserID, $facebookInstance){
        if(!isset(self::$instances[$fbUserID])){
            self::$instances[$fbUserID] = new self($fbUserID, $facebookInstance);
        }

        return self::$instances[$fbUserID];
    }

    public function __construct($userId, $FB_API){
        $this->FB_USER_ID = $userId;
        $this->FB_API = $FB_API;

        $access_token = $this->FB_API->API->getAccessToken();

        $options = array(
            'uid' => $this->FB_USER_ID,
            'app_id' => $this->FB_API->APP_ID,
            'server' => self::CHAT_SERVER
        );

        if ($this->xmppConnect($options, $access_token)) {
            if(self::$debug){
                print "Connection successful\n";
            }
        } else {
            if(self::$debug){
                print "An error ocurred\n";
            }
        }
    }

    private function openConnection($server) {
        if(self::$debug){
            print "[INFO] Opening connection... ";
        }

        $this->fsocket = fsockopen($server, 5222, $errno, $errstr);
        if(self::$debug){
            if (!$this->fsocket) {
                print "$errstr ($errno)\n";
            } else {
                print "connnection open\n";
            }
        }

        return true;
    }

    private function sendXML($fp, $xml) {
        //print_r($xml, true)."\n";
        $a = fwrite($fp, $xml);
        //print_r($a, true)." \n\n";
    }

    private function receiveXML($fp,  $size=8096) {
        $xml = fread($fp, $size);
        if ($xml === "") {
            return null;
        }
        if (!preg_match('/^</', $xml)) {
            $xml = '<' . $xml;
        }
        if(self::$debug){
            echo "\n".$xml."\n";
        }

        // parses xml
        $xml_parser = xml_parser_create();
        xml_parse_into_struct($xml_parser, $xml, $val, $index);
        xml_parser_free($xml_parser);

        return array($val, $index);
    }

    private function findXMPP($fp,  $tag, $value=null, &$ret=null) {
        static $val = null, $index = null;

        do {
            if ($val === null && $index === null) {
                list($val, $index) = $this->receiveXML($fp);
                if(self::$debug){
                    print_r($val);
                    print_r($index);
                }
                if ($val === null || $index === null) {
                    return false;
                }
            }

            foreach ($index as $tag_key => $tag_array) {
                if ($tag_key === $tag) {
                    if ($value === null) {
                        if (isset($val[$tag_array[0]]['value'])) {
                            $ret = $val[$tag_array[0]]['value'];
                        }
                        return true;
                    }
                    foreach ($tag_array as $i => $pos) {
                        if ($val[$pos]['tag'] === $tag && isset($val[$pos]['value']) &&
                            $val[$pos]['value'] === $value) {
                            $ret = $val[$pos]['value'];
                            return true;
                        }
                    }
                }
            }
            $val = $index = null;
        } while (!feof($fp));

        return false;
    }


    private function xmppConnect($options, $access_token) {

        $this->openConnection($options['server']);

        if (!$this->fsocket) {
            return false;
        }
        // initiates auth process (using X-FACEBOOK_PLATFORM)
        $this->sendXML($this->fsocket,  $this->STREAM_XML);
        if (!$this->findXMPP($this->fsocket, 'STREAM:STREAM')) {
            return false;
        }
        if (!$this->findXMPP($this->fsocket,  'MECHANISM', 'X-FACEBOOK-PLATFORM')) {
            return false;
        }

        // starting tls - MANDATORY TO USE OAUTH TOKEN!!!!
        $this->sendXML($this->fsocket,  $this->START_TLS);
        $proceed = '';
        if (!$this->findXMPP($this->fsocket, 'PROCEED', null, $proceed)) {
            return false;
        }
        stream_socket_enable_crypto($this->fsocket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);


        $this->sendXML($this->fsocket, $this->STREAM_XML);
        if (!$this->findXMPP($this->fsocket, 'STREAM:STREAM')) {
            return false;
        }
        if (!$this->findXMPP($this->fsocket, 'MECHANISM', 'X-FACEBOOK-PLATFORM')) {
            return false;
        }


        // gets challenge from server and decode it
        $this->sendXML($this->fsocket, $this->AUTH_XML);
        echo ' sent ';
        $challenge = null;
        if (!$this->findXMPP($this->fsocket,  'CHALLENGE', null, $challenge)) {
            return false;
        }
        echo 'C: '.$challenge."\n";

        $challenge = base64_decode($challenge);
        $challenge = urldecode($challenge);
        parse_str($challenge, $challenge_array);

        // creates the response array

        $access_token = $this->FB_API->API->getAccessToken();
        $resp_array = array(
            'method' => $challenge_array['method'],
            'nonce' => $challenge_array['nonce'],
            'access_token' => $access_token,
            'api_key' => $options['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->sendXML($this->fsocket, $xml);
        if (!$this->findXMPP($this->fsocket, 'SUCCESS')) {
            return false;
        }

        // finishes auth process
        $this->sendXML($this->fsocket, $this->STREAM_XML);
        if (!$this->findXMPP($this->fsocket,'STREAM:STREAM')) {
            return false;
        }

        if (!$this->findXMPP($this->fsocket, 'STREAM:FEATURES')) {
            return false;
        }
        $this->sendXML($this->fsocket, $this->RESOURCE_XML);
        if (!$this->findXMPP($this->fsocket, 'JID')) {
            return false;
        }

        $this->sendXML($this->fsocket, $this->SESSION_XML);
        if (!$this->findXMPP($this->fsocket, 'SESSION')) {
            return false;
        }

        return true;
    }

    private function xmppDisconnect(){
        $this->sendXML($this->fsocket, $this->CLOSE_XML);
        if(self::$debug){
            print ("Authentication complete\n");
        }
        return fclose($this->fsocket);
    }

    public function sendMessage($receiverId, $message){
        $xml = sprintf($this->MESSAGE_XML, $this->FB_USER_ID, $receiverId, $message);
        echo $xml."\n";

        $this->sendXML($this->fsocket, $xml);
        if ($this->findXMPP($this->fsocket, 'BODY')) {
            return true;
        } else {
            return false;
        }
    }

    public static function toggleDebug(){
        self::$debug = !self::$debug;
    }

    public function __destruct(){
        if($this->fsocket){
            $this->xmppDisconnect();
        }
    }
} 
4

0 回答 0