void TestPackets::testStreamFeatures()
{
    const QByteArray xml("<stream:features/>");
    QXmppStreamFeatures features;
    parsePacket(features, xml);
    QCOMPARE(features.bindMode(), QXmppStreamFeatures::Disabled);
    QCOMPARE(features.sessionMode(), QXmppStreamFeatures::Disabled);
    QCOMPARE(features.nonSaslAuthMode(), QXmppStreamFeatures::Disabled);
    QCOMPARE(features.tlsMode(), QXmppStreamFeatures::Disabled);
    QCOMPARE(features.authMechanisms(), QList<QXmppConfiguration::SASLAuthMechanism>());
    QCOMPARE(features.compressionMethods(), QList<QXmppConfiguration::CompressionMethod>());
    serializePacket(features, xml);

    const QByteArray xml2("<stream:features>"
        "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>"
        "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>"
        "<auth xmlns=\"http://jabber.org/features/iq-auth\"/>"
        "<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>"
        "<compression xmlns=\"http://jabber.org/features/compress\"><method>zlib</method></compression>"
        "<mechanisms xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"><mechanism>PLAIN</mechanism></mechanisms>"
        "</stream:features>");
    QXmppStreamFeatures features2;
    parsePacket(features2, xml2);
    QCOMPARE(features2.bindMode(), QXmppStreamFeatures::Enabled);
    QCOMPARE(features2.sessionMode(), QXmppStreamFeatures::Enabled);
    QCOMPARE(features2.nonSaslAuthMode(), QXmppStreamFeatures::Enabled);
    QCOMPARE(features2.tlsMode(), QXmppStreamFeatures::Enabled);
    QCOMPARE(features2.authMechanisms(), QList<QXmppConfiguration::SASLAuthMechanism>() << QXmppConfiguration::SASLPlain);
    QCOMPARE(features2.compressionMethods(), QList<QXmppConfiguration::CompressionMethod>() << QXmppConfiguration::ZlibCompression);
    serializePacket(features2, xml2);
}
void tst_QXmppStreamFeatures::testEmpty()
{
    const QByteArray xml("<stream:features/>");

    QXmppStreamFeatures features;
    parsePacket(features, xml);
    QCOMPARE(features.bindMode(), QXmppStreamFeatures::Disabled);
    QCOMPARE(features.sessionMode(), QXmppStreamFeatures::Disabled);
    QCOMPARE(features.nonSaslAuthMode(), QXmppStreamFeatures::Disabled);
    QCOMPARE(features.tlsMode(), QXmppStreamFeatures::Disabled);
    QCOMPARE(features.authMechanisms(), QStringList());
    QCOMPARE(features.compressionMethods(), QStringList());
    serializePacket(features, xml);
}
void tst_QXmppStreamFeatures::testFull()
{
    const QByteArray xml("<stream:features>"
        "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/>"
        "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/>"
        "<auth xmlns=\"http://jabber.org/features/iq-auth\"/>"
        "<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>"
        "<compression xmlns=\"http://jabber.org/features/compress\"><method>zlib</method></compression>"
        "<mechanisms xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"><mechanism>PLAIN</mechanism></mechanisms>"
        "</stream:features>");

    QXmppStreamFeatures features;
    parsePacket(features, xml);
    QCOMPARE(features.bindMode(), QXmppStreamFeatures::Enabled);
    QCOMPARE(features.sessionMode(), QXmppStreamFeatures::Enabled);
    QCOMPARE(features.nonSaslAuthMode(), QXmppStreamFeatures::Enabled);
    QCOMPARE(features.tlsMode(), QXmppStreamFeatures::Enabled);
    QCOMPARE(features.authMechanisms(), QStringList() << "PLAIN");
    QCOMPARE(features.compressionMethods(), QStringList() << "zlib");
    serializePacket(features, xml);
}
Exemple #4
0
void QXmppOutgoingClient::handleStreamFeaturesOrDisconnect(const QDomElement& nodeRecv)
{
    if (!QXmppStreamFeatures::isStreamFeatures(nodeRecv)) {
        warning("unexpected node received, stream features expected");
        disconnectFromHost();
        return;
    }

    QXmppStreamFeatures features;
    features.parse(nodeRecv);

    if (!socket()->isEncrypted()) {
        // determine TLS mode to use
        const QXmppConfiguration::StreamSecurityMode localSecurity = configuration().streamSecurityMode();
        const QXmppStreamFeatures::Mode remoteSecurity = features.tlsMode();
        if (!socket()->supportsSsl() &&
            (localSecurity == QXmppConfiguration::TLSRequired ||
             remoteSecurity == QXmppStreamFeatures::Required))
        {
            warning("Disconnecting as TLS is required, but SSL support is not available");
            disconnectFromHost();
            return;
        }
        if (localSecurity == QXmppConfiguration::TLSRequired &&
          remoteSecurity == QXmppStreamFeatures::Disabled)
        {
            warning("Disconnecting as TLS is required, but not supported by the server");
            disconnectFromHost();
            return;
        }

        if (socket()->supportsSsl() &&
            localSecurity != QXmppConfiguration::TLSDisabled &&
            remoteSecurity != QXmppStreamFeatures::Disabled)
        {
            // enable TLS as it is support by both parties
            d->state = QXmppOutgoingClientPrivate::StartingTLS;
            sendData("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
            return;
        }
    }

    // handle authentication
    const bool nonSaslAvailable = features.nonSaslAuthMode() != QXmppStreamFeatures::Disabled;
    const bool saslAvailable = !features.authMechanisms().isEmpty();
    if (saslAvailable && configuration().useSASLAuthentication())
    {
        // supported and preferred SASL auth mechanisms
        QStringList supportedMechanisms = QXmppSaslClient::availableMechanisms();
        const QString preferredMechanism = configuration().saslAuthMechanism();
        if (configuration().facebookAppId().isEmpty() || configuration().facebookAccessToken().isEmpty())
            supportedMechanisms.removeAll("X-FACEBOOK-PLATFORM");
        if (configuration().windowsLiveAccessToken().isEmpty())
            supportedMechanisms.removeAll("X-MESSENGER-OAUTH2");
        if (configuration().googleAccessToken().isEmpty())
            supportedMechanisms.removeAll("X-OAUTH2");

        // determine SASL Authentication mechanism to use
        QStringList commonMechanisms;
        QString usedMechanism;
        foreach (const QString &mechanism, features.authMechanisms()) {
            if (supportedMechanisms.contains(mechanism))
                commonMechanisms << mechanism;
        }
        if (commonMechanisms.isEmpty()) {
            warning("No supported SASL Authentication mechanism available");
            disconnectFromHost();
            return;
        } else if (!commonMechanisms.contains(preferredMechanism)) {
            info(QString("Desired SASL Auth mechanism '%1' is not available, selecting first available one").arg(preferredMechanism));
            usedMechanism = commonMechanisms.first();
        } else {
            usedMechanism = preferredMechanism;
        }

        d->saslClient = QXmppSaslClient::create(usedMechanism, this);
        if (!d->saslClient) {
            warning("SASL mechanism negotiation failed");
            disconnectFromHost();
            return;
        }
        info(QString("SASL mechanism '%1' selected").arg(d->saslClient->mechanism()));
        d->saslClient->setHost(d->config.domain());
        d->saslClient->setServiceType("xmpp");
        if (d->saslClient->mechanism() == "X-FACEBOOK-PLATFORM") {
            d->saslClient->setUsername(configuration().facebookAppId());
            d->saslClient->setPassword(configuration().facebookAccessToken());
        } else if (d->saslClient->mechanism() == "X-MESSENGER-OAUTH2") {
            d->saslClient->setPassword(configuration().windowsLiveAccessToken());
        } else if (d->saslClient->mechanism() == "X-OAUTH2") {
            d->saslClient->setUsername(configuration().user());
            d->saslClient->setPassword(configuration().googleAccessToken());
        } else {
            d->saslClient->setUsername(configuration().user());
            d->saslClient->setPassword(configuration().password());
        }

        // send SASL auth request
        QByteArray response;
        if (!d->saslClient->respond(QByteArray(), response)) {
            warning("SASL initial response failed");
            disconnectFromHost();
            return;
        }
        d->state = QXmppOutgoingClientPrivate::Authenticating;
        sendPacket(QXmppSaslAuth(d->saslClient->mechanism(), response));
        return;
    } else if(nonSaslAvailable && configuration().useNonSASLAuthentication()) {