void QXmppIncomingClient::handleStream(const QDomElement &streamElement)
{
    if (d->idleTimer->interval())
        d->idleTimer->start();
    d->saslStep = 0;

    // start stream
    const QByteArray sessionId = generateStanzaHash().toAscii();
    QString response = QString("<?xml version='1.0'?><stream:stream"
        " xmlns=\"%1\" xmlns:stream=\"%2\""
        " id=\"%3\" from=\"%4\" version=\"1.0\" xml:lang=\"en\">").arg(
        ns_client,
        ns_stream,
        sessionId,
        d->domain.toAscii());
    sendData(response.toUtf8());

    // check requested domain
    if (streamElement.attribute("to") != d->domain)
    {
        QString response = QString("<stream:error>"
            "<host-unknown xmlns=\"urn:ietf:params:xml:ns:xmpp-streams\"/>"
            "<text xmlns=\"urn:ietf:params:xml:ns:xmpp-streams\">"
                "This server does not serve %1"
            "</text>"
            "</stream:error>").arg(streamElement.attribute("to"));
        sendData(response.toUtf8());
        disconnectFromHost();
        return;
    }

    // send stream features
    QXmppStreamFeatures features;
    if (socket() && !socket()->isEncrypted() && !socket()->localCertificate().isNull() && !socket()->privateKey().isNull())
        features.setTlsMode(QXmppStreamFeatures::Enabled);
    if (!d->username.isEmpty())
    {
        features.setBindMode(QXmppStreamFeatures::Required);
        features.setSessionMode(QXmppStreamFeatures::Enabled);
    }
    else if (d->passwordChecker)
    {
        QList<QXmppConfiguration::SASLAuthMechanism> mechanisms;
        mechanisms << QXmppConfiguration::SASLPlain;
        if (d->passwordChecker->hasGetPassword())
            mechanisms << QXmppConfiguration::SASLDigestMD5;
        features.setAuthMechanisms(mechanisms);
    }
    sendPacket(features);
}
Пример #2
0
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);
}
/// \cond
void QXmppIncomingServer::handleStream(const QDomElement &streamElement)
{
    const QString from = streamElement.attribute("from");
    if (!from.isEmpty())
        info(QString("Incoming server stream from %1 on %2").arg(from, d->origin()));

    // start stream
    d->localStreamId = QXmppUtils::generateStanzaHash().toLatin1();
    QString data = QString("<?xml version='1.0'?><stream:stream"
        " xmlns='%1' xmlns:db='%2' xmlns:stream='%3'"
        " id='%4' version=\"1.0\">").arg(
            ns_server,
            ns_server_dialback,
            ns_stream,
            d->localStreamId);
    sendData(data.toUtf8());

    // send stream features
    QXmppStreamFeatures features;
    if (!socket()->isEncrypted() && !socket()->localCertificate().isNull() && !socket()->privateKey().isNull())
        features.setTlsMode(QXmppStreamFeatures::Enabled);
    sendPacket(features);
}
Пример #4
0
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);
}
Пример #5
0
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);
}
Пример #6
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()) {
Пример #7
0
void QXmppOutgoingServer::handleStanza(const QDomElement &stanza)
{
    const QString ns = stanza.namespaceURI();

    if(QXmppStreamFeatures::isStreamFeatures(stanza))
    {
        QXmppStreamFeatures features;
        features.parse(stanza);

        if (!socket()->isEncrypted())
        {
            // check we can satisfy TLS constraints
            if (!socket()->supportsSsl() &&
                 features.tlsMode() == QXmppStreamFeatures::Required)
            {
                warning("Disconnecting as TLS is required, but SSL support is not available");
                disconnectFromHost();
                return;
            }

            // enable TLS if possible
            if (socket()->supportsSsl() &&
                features.tlsMode() != QXmppStreamFeatures::Disabled)
            {
                sendData("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
                return;
            }
        }

        // send dialback if needed
        d->dialbackTimer->stop();
        sendDialback();
    }
    else if (ns == ns_tls)
    {
        if (stanza.tagName() == QLatin1String("proceed"))
        {
            debug("Starting encryption");
            socket()->startClientEncryption();
            return;
        }
    }
    else if (QXmppDialback::isDialback(stanza))
    {
        QXmppDialback response;
        response.parse(stanza);

        // check the request is valid
        if (response.from().isEmpty() ||
            response.to() != d->localDomain ||
            response.type().isEmpty())
        {
            warning("Invalid dialback response received");
            return;
        }
        if (response.command() == QXmppDialback::Result)
        {
            if (response.type() == QLatin1String("valid"))
            {
                info(QString("Outgoing server stream to %1 is ready").arg(response.from()));
                d->ready = true;

                // send queued data
                Q_FOREACH (const QByteArray &data, d->dataQueue)
                    sendData(data);
                d->dataQueue.clear();

                // emit signal
                Q_EMIT connected();
            }
        }
        else if (response.command() == QXmppDialback::Verify)