aboutsummaryrefslogtreecommitdiff
path: root/src/QXmppIncomingClient.cpp
diff options
context:
space:
mode:
authorJeremy Lainé <jeremy.laine@m4x.org>2011-04-07 18:27:51 +0000
committerJeremy Lainé <jeremy.laine@m4x.org>2011-04-07 18:27:51 +0000
commit194762951e6a59808350032f38e05cd0e5d03807 (patch)
treec766a97835dad98838906710be77a2197099e1e9 /src/QXmppIncomingClient.cpp
parentfafebd0f90dd8b6fe311e0384898653632072539 (diff)
downloadqxmpp-194762951e6a59808350032f38e05cd0e5d03807.tar.gz
make password checking full asynchronous
Diffstat (limited to 'src/QXmppIncomingClient.cpp')
-rw-r--r--src/QXmppIncomingClient.cpp189
1 files changed, 81 insertions, 108 deletions
diff --git a/src/QXmppIncomingClient.cpp b/src/QXmppIncomingClient.cpp
index cdee467c..7b8ce038 100644
--- a/src/QXmppIncomingClient.cpp
+++ b/src/QXmppIncomingClient.cpp
@@ -21,7 +21,6 @@
*
*/
-#include <QCryptographicHash>
#include <QDomElement>
#include <QSslKey>
#include <QSslSocket>
@@ -30,6 +29,7 @@
#include "QXmppBindIq.h"
#include "QXmppConstants.h"
#include "QXmppMessage.h"
+#include "QXmppPasswordChecker.h"
#include "QXmppSaslAuth.h"
#include "QXmppSessionIq.h"
#include "QXmppStreamFeatures.h"
@@ -76,7 +76,7 @@ QXmppIncomingClient::QXmppIncomingClient(QSslSocket *socket, const QString &doma
d->idleTimer = new QTimer(this);
d->idleTimer->setSingleShot(true);
bool check = connect(d->idleTimer, SIGNAL(timeout()),
- this, SLOT(slotTimeout()));
+ this, SLOT(onTimeout()));
Q_ASSERT(check);
Q_UNUSED(check);
}
@@ -214,33 +214,22 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
return;
}
- const QString username = QString::fromUtf8(auth[1]);
- const QString password = QString::fromUtf8(auth[2]);
+ QXmppPasswordRequest request;
+ request.setDomain(d->domain);
+ request.setUsername(QString::fromUtf8(auth[1]));
+ request.setPassword(QString::fromUtf8(auth[2]));
if (!d->passwordChecker) {
// FIXME: what type of failure?
- warning(QString("Cannot authenticate '%1', no password checker").arg(username));
+ warning(QString("Cannot authenticate '%1', no password checker").arg(request.username()));
sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
disconnectFromHost();
return;
}
- QXmppPasswordChecker::Error error = d->passwordChecker->checkPassword(username, d->domain, password);
- if (error == QXmppPasswordChecker::NoError)
- {
- d->username = username;
- info(QString("Authentication succeeded for '%1'").arg(d->username));
- sendData("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
- } else if (error == QXmppPasswordChecker::AuthorizationError) {
- warning(QString("Authentication failed for '%1'").arg(username));
- sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>");
- disconnectFromHost();
- return;
- } else {
- warning(QString("Temporary authentication failure for '%1'").arg(username));
- sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><temporary-auth-failure/></failure>");
- disconnectFromHost();
- return;
- }
+ QXmppPasswordReply *reply = d->passwordChecker->checkPassword(request);
+ reply->setParent(this);
+ reply->setProperty("__sasl_username", request.username());
+ connect(reply, SIGNAL(finished()), this, SLOT(onPasswordReply()));
}
else if (mechanism == "DIGEST-MD5")
{
@@ -269,13 +258,13 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
}
else if (nodeRecv.tagName() == "response")
{
- const QByteArray raw = QByteArray::fromBase64(nodeRecv.text().toAscii());
- QMap<QByteArray, QByteArray> response = QXmppSaslDigestMd5::parseMessage(raw);
-
if (d->saslStep == 1)
{
+ const QByteArray raw = QByteArray::fromBase64(nodeRecv.text().toAscii());
+ QMap<QByteArray, QByteArray> saslResponse = QXmppSaslDigestMd5::parseMessage(raw);
+
// check credentials
- const QString username = QString::fromUtf8(response.value("username"));
+ const QString username = QString::fromUtf8(saslResponse.value("username"));
if (!d->passwordChecker) {
// FIXME: what type of failure?
warning(QString("Cannot authenticate '%1', no password checker").arg(username));
@@ -284,34 +273,14 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
return;
}
- QByteArray secret;
- QXmppPasswordChecker::Error error = d->passwordChecker->getDigest(username, d->domain, secret);
- if (error == QXmppPasswordChecker::TemporaryError) {
- warning(QString("Temporary authentication failure for '%1'").arg(username));
- sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><temporary-auth-failure/></failure>");
- disconnectFromHost();
- return;
- }
- d->saslDigest.setSecret(secret);
- d->saslDigest.setDigestUri(response.value("digest-uri"));
- d->saslDigest.setNc(response.value("nc"));
- d->saslDigest.setCnonce(response.value("cnonce"));
- if (response["response"] != d->saslDigest.calculateDigest(
- QByteArray("AUTHENTICATE:") + d->saslDigest.digestUri()))
- {
- sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>");
- disconnectFromHost();
- return;
- }
+ QXmppPasswordRequest request;
+ request.setUsername(username);
+ request.setDomain(d->domain);
- // send new challenge
- d->username = username;
- d->saslStep = 2;
- QMap<QByteArray, QByteArray> challenge;
- challenge["rspauth"] = d->saslDigest.calculateDigest(
- QByteArray(":") + d->saslDigest.digestUri());
- const QByteArray data = QXmppSaslDigestMd5::serializeMessage(challenge).toBase64();
- sendData("<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" + data +"</challenge>");
+ QXmppPasswordReply *reply = d->passwordChecker->getDigest(request);
+ reply->setParent(this);
+ reply->setProperty("__sasl_raw", raw);
+ connect(reply, SIGNAL(finished()), this, SLOT(onDigestReply()));
}
else if (d->saslStep == 2)
{
@@ -395,71 +364,75 @@ void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
}
}
-void QXmppIncomingClient::slotTimeout()
+void QXmppIncomingClient::onDigestReply()
{
- warning(QString("Idle timeout for %1").arg(jid()));
- disconnectFromHost();
-}
+ QXmppPasswordReply *reply = qobject_cast<QXmppPasswordReply*>(sender());
+ if (!reply)
+ return;
+ reply->deleteLater();
-/// Checks that the given credentials are valid.
-///
-/// The base implementation requires that you reimplement getPassword().
-///
-/// \param username
-/// \param domain
-/// \param password
+ const QMap<QByteArray, QByteArray> saslResponse = QXmppSaslDigestMd5::parseMessage(reply->property("__sasl_raw").toByteArray());
+ const QString username = QString::fromUtf8(saslResponse.value("username"));
+ if (reply->error() == QXmppPasswordReply::TemporaryError) {
+ warning(QString("Temporary authentication failure for '%1'").arg(username));
+ sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><temporary-auth-failure/></failure>");
+ disconnectFromHost();
+ return;
+ }
-QXmppPasswordChecker::Error QXmppPasswordChecker::checkPassword(const QString &username, const QString &domain, const QString &password)
-{
- QString secret;
- Error error = getPassword(username, domain, secret);
- if (error != NoError)
- return error;
+ d->saslDigest.setSecret(reply->digest());
+ d->saslDigest.setDigestUri(saslResponse.value("digest-uri"));
+ d->saslDigest.setNc(saslResponse.value("nc"));
+ d->saslDigest.setCnonce(saslResponse.value("cnonce"));
+ if (saslResponse.value("response") != d->saslDigest.calculateDigest(
+ QByteArray("AUTHENTICATE:") + d->saslDigest.digestUri()))
+ {
+ sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>");
+ disconnectFromHost();
+ return;
+ }
- return (password == secret) ? NoError : AuthorizationError;
+ // send new challenge
+ d->username = username;
+ d->saslStep = 2;
+ QMap<QByteArray, QByteArray> challenge;
+ challenge["rspauth"] = d->saslDigest.calculateDigest(
+ QByteArray(":") + d->saslDigest.digestUri());
+ const QByteArray data = QXmppSaslDigestMd5::serializeMessage(challenge).toBase64();
+ sendData("<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" + data +"</challenge>");
}
-/// Retrieves the MD5 digest for the given username.
-///
-/// Reimplement this method if your backend natively supports
-/// retrieving MD5 digests.
-///
-/// \param username
-
-QXmppPasswordChecker::Error QXmppPasswordChecker::getDigest(const QString &username, const QString &domain, QByteArray &digest)
+void QXmppIncomingClient::onPasswordReply()
{
- QString secret;
- Error error = getPassword(username, domain, secret);
- if (error != NoError)
- return error;
-
- digest = QCryptographicHash::hash(
- (username + ":" + domain + ":" + secret).toUtf8(),
- QCryptographicHash::Md5);
- return NoError;
+ QXmppPasswordReply *reply = qobject_cast<QXmppPasswordReply*>(sender());
+ if (!reply)
+ return;
+ reply->deleteLater();
+
+ const QString username = reply->property("__sasl_username").toString();
+ switch (reply->error()) {
+ case QXmppPasswordReply::NoError:
+ d->username = username;
+ info(QString("Authentication succeeded for '%1'").arg(username));
+ sendData("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
+ break;
+ case QXmppPasswordReply::AuthorizationError:
+ warning(QString("Authentication failed for '%1'").arg(username));
+ sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><not-authorized/></failure>");
+ disconnectFromHost();
+ break;
+ case QXmppPasswordReply::TemporaryError:
+ warning(QString("Temporary authentication failure for '%1'").arg(username));
+ sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><temporary-auth-failure/></failure>");
+ disconnectFromHost();
+ break;
+ }
}
-/// Retrieves the password for the given username.
-///
-/// The simplest way to write a password checker is to reimplement this method.
-///
-/// \param username
-/// \param domain
-/// \param password
-
-QXmppPasswordChecker::Error QXmppPasswordChecker::getPassword(const QString &username, const QString &domain, QString &password)
+void QXmppIncomingClient::onTimeout()
{
- Q_UNUSED(username);
- Q_UNUSED(domain);
- Q_UNUSED(password);
- return TemporaryError;
+ warning(QString("Idle timeout for %1").arg(jid()));
+ disconnectFromHost();
}
-/// Returns true if the getPassword() method is implemented.
-///
-
-bool QXmppPasswordChecker::hasGetPassword() const
-{
- return false;
-}