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); }
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); }
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); }
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()) {
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)