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