IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

XMPP ライブラリ Gloox のトランスポート層を触る(2)

SASL → Resource Bind → Create Session まで

#include <cassert>
#include <iostream>
#include <boost/make_shared.hpp>
#include <gloox/iq.h>
#include <gloox/parser.h>
#include <gloox/base64.h>
#include <gloox/connectiontcpclient.h>
#include <gloox/connectiontls.h>

class Bot : public gloox::ConnectionDataHandler, gloox::TagHandler, gloox::TLSHandler
{
public:
    Bot() :
        client_(new gloox::ConnectionTCPClient(this, log_, "talk.google.com", 5222)),
        raw_client_(client_),
        parser_(this)
    {
        gloox::ConnectionError ret = client_->connect();

        assert(ret == gloox::ConnNoError);

        client_->receive();
    }

    virtual void handleReceivedData(const gloox::ConnectionBase* con, const std::string& data)
    {
        std::cerr << "[recv] " << data << std::endl;
        std::string copied = data;
        int pos = parser_.feed(copied);

        assert(pos < 0);
    }

    virtual void handleConnect(const gloox::ConnectionBase* con)
    {
        send(
            "<?xml version='1.0' ?>"
            "<stream:stream "
                "xmlns:stream=\"http://etherx.jabber.org/streams\" "
                "version=\"1.0\" xmlns=\"jabber:client\" "
                "to=\"gmail.com\" "
                "xml:lang=\"en\" "
                "xmlns:xml=\"http://www.w3.org/XML/1998/namespace\" >");
    }

    virtual void handleTag(gloox::Tag* tag)
    {
        if (tag->name() == "stream" && tag->xmlns() == "http://etherx.jabber.org/streams") {
            sid_ = tag->findAttribute( "id" );
        }
        else {
            if(tag->name() == "features" && tag->xmlns() == "http://etherx.jabber.org/streams")
            {
                if(tag->hasChild("starttls", "xmlns", "urn:ietf:params:xml:ns:xmpp-tls") )
                {
                    send(gloox::Tag("starttls", "xmlns", "urn:ietf:params:xml:ns:xmpp-tls").xml());
                }
                else if(tag->hasChild("mechanisms", "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl") &&
                        tag->findChild("mechanisms")->hasChildWithCData("mechanism", "PLAIN")) // only PLAIN :(
                {
                    gloox::Tag a("auth", "xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
                    a.addAttribute("mechanism", "PLAIN");

                    std::string tmp;
                    tmp += '\0';
                    tmp += "seijro";
                    tmp += '\0';
                    tmp += "PASSWORD";

                    a.setCData(gloox::Base64::encode64(tmp));
                    send(a.xml());
                }
                else if(tag->hasChild("bind", "xmlns", "urn:ietf:params:xml:ns:xmpp-bind"))
                {
                    gloox::Tag iq("iq", "xmlns", "jabber:client");
                    iq.addAttribute("type", "set");
                    iq.addAttribute("id", "binding");
                    gloox::Tag *bind = new gloox::Tag("bind", "xmlns", "urn:ietf:params:xml:ns:xmpp-bind");
                    gloox::Tag *resource = new gloox::Tag("resource");
                    resource->setCData("myresource");
                    bind->addChild(resource);
                    iq.addChild(bind);

                    send(iq.xml());
                }

            }

            else if(tag->name() == "proceed" && tag->xmlns() == "urn:ietf:params:xml:ns:xmpp-tls")
            {
                gloox::ConnectionTLS* encryption_client = new gloox::ConnectionTLS(this, client_.get(), log_);
                client_.reset(encryption_client);
                encryption_client->registerTLSHandler(this);
                gloox::ConnectionError ret = client_->connect();
                assert(ret == gloox::ConnNoError);
            }

            else if(tag->name() == "success" && tag->xmlns() == "urn:ietf:params:xml:ns:xmpp-sasl")
            {
                send(
                    "<?xml version='1.0' ?>"
                    "<stream:stream "
                        "xmlns:stream=\"http://etherx.jabber.org/streams\" "
                        "version=\"1.0\" xmlns=\"jabber:client\" "
                        "to=\"gmail.com\" "
                        "xml:lang=\"en\" "
                        "xmlns:xml=\"http://www.w3.org/XML/1998/namespace\" >");
            }

            else if (tag->name() == "iq")
            {
                if (tag->hasChild("bind", "xmlns", "urn:ietf:params:xml:ns:xmpp-bind"))
                {
                    resource_ = tag->findChild("bind")->findChild("jid")->cdata();

                    gloox::Tag iq("iq", "xmlns", "jabber:client");
                    iq.addAttribute("type", "set");
                    iq.addAttribute("id", "create_session");
                    iq.addChild(new gloox::Tag("session", "xmlns", "urn:ietf:params:xml:ns:xmpp-session"));

                    send(iq.xml());
                }
            }
        }
    }

    virtual void handleEncryptedData(const gloox::TLSBase* tls, const std::string& data)
    {
        std::cout << "handleEncryptedData" << std::endl;
    }

    virtual void handleDecryptedData(const gloox::TLSBase* tls, const std::string& data)
    {
        std::cout << "handleDecryptedData" << std::endl;
    }

    virtual void handleHandshakeResult(const gloox::TLSBase* tls, bool, gloox::CertInfo& cert)
    {
        std::cout << "handleHandshakeResult" << std::endl;
    }

    virtual void handleDisconnect(const gloox::ConnectionBase* con, gloox::ConnectionError)
    {
        std::cout << "handleDisonnect" << std::endl;
    }

private:

    void send(const std::string &data)
    {
        std::cerr << "[send] " << data << std::endl;
        client_->send(data);
    }

    gloox::LogSink log_;

    boost::shared_ptr<gloox::ConnectionBase> client_;

    boost::shared_ptr<gloox::ConnectionBase> raw_client_;

    gloox::Parser parser_;

    std::string sid_;

    std::string resource_;
};

int main()
{
    Bot bot;
}
[send] <?xml version='1.0' ?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" version="1.0" xmlns="jabber:client" to="gmail.com" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" >
[recv] <stream:stream from="gmail.com" id="7D3EB506BD78CED4" version="1.0" xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client"><stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"><required/></starttls><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>X-GOOGLE-TOKEN</mechanism></mechanisms></stream:features>
[send] <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
[recv] <proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>
handleHandshakeResult
[send] <?xml version='1.0' ?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" version="1.0" xmlns="jabber:client" to="gmail.com" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" >
[recv] <stream:stream from="gmail.com" id="B2A71EB8DE19C974" version="1.0" xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client">
[recv] <stream:features><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>PLAIN</mechanism><mechanism>X-GOOGLE-TOKEN</mechanism></mechanisms></stream:features>
[send] <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>XXXXXXXXXXXXXXXXXXXXXXX</auth>
[recv] <success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>
[send] <?xml version='1.0' ?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" version="1.0" xmlns="jabber:client" to="gmail.com" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" >
[recv] <stream:stream from="gmail.com" id="0522E247621D8813" version="1.0" xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client">
[recv] <stream:features><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></stream:features>
[send] <iq xmlns='jabber:client' type='set' id='binding'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>myresource</resource></bind></iq>
[recv] <iq id="binding" type="result"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>seijro@gmail.com/myresource65631060</jid></bind></iq>
[send] <iq xmlns='jabber:client' type='set' id='create_session'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>
[recv]  
[recv] <iq type="result" id="create_session"/>
[recv]  
[recv] <message type="chat" to="seijro@gmail.com/myresource65631060" id="test_002" from="seijro@gmail.com/amachang4B803057"><body>Hello, World!!</body></message>