bufferviewoverlay.cpp
client.cpp
clientaliasmanager.cpp
+ clientauthhandler.cpp
clientbacklogmanager.cpp
clientbufferviewconfig.cpp
clientbufferviewmanager.cpp
bufferviewoverlay.h
client.h
clientaliasmanager.h
+ clientauthhandler.h
clientbacklogmanager.h
clientbufferviewconfig.h
clientbufferviewmanager.h
_messageModel(0),
_messageProcessor(0),
_coreAccountModel(new CoreAccountModel(this)),
- _coreConnection(new CoreConnection(_coreAccountModel, this)),
+ _coreConnection(new CoreConnection(this)),
_connected(false),
_debugLog(&_debugLogBuffer)
{
#include <QPointer>
#include "bufferinfo.h"
+#include "coreaccount.h"
#include "coreconnection.h"
#include "quassel.h"
#include "types.h"
static inline CoreAccount currentCoreAccount() { return coreConnection()->currentAccount(); }
static inline Quassel::Features coreFeatures() { return _coreFeatures; }
+ static void setCoreFeatures(Quassel::Features features);
+
static bool isConnected();
static bool internalCore();
void init();
static void addNetwork(Network *);
- static void setCoreFeatures(Quassel::Features);
static inline BufferSyncer *bufferSyncer() { return instance()->_bufferSyncer; }
static QPointer<Client> instanceptr;
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-2013 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "clientauthhandler.h"
+
+// TODO: support system application proxy (new in Qt 4.6)
+
+#ifdef HAVE_SSL
+ #include <QSslSocket>
+#else
+ #include <QTcpSocket>
+#endif
+
+#include "client.h"
+#include "clientsettings.h"
+
+#include "protocols/legacy/legacypeer.h"
+
+using namespace Protocol;
+
+ClientAuthHandler::ClientAuthHandler(CoreAccount account, QObject *parent)
+ : AuthHandler(parent),
+ _peer(0),
+ _account(account)
+{
+
+}
+
+
+void ClientAuthHandler::connectToCore()
+{
+ CoreAccountSettings s;
+
+#ifdef HAVE_SSL
+ QSslSocket *socket = new QSslSocket(this);
+ // make sure the warning is shown if we happen to connect without SSL support later
+ s.setAccountValue("ShowNoClientSslWarning", true);
+#else
+ if (_account.useSsl()) {
+ if (s.accountValue("ShowNoClientSslWarning", true).toBool()) {
+ bool accepted = false;
+ emit handleNoSslInClient(&accepted);
+ if (!accepted) {
+ emit errorMessage(tr("Unencrypted connection canceled"));
+ return;
+ }
+ s.setAccountValue("ShowNoClientSslWarning", false);
+ }
+ }
+ QTcpSocket *socket = new QTcpSocket(this);
+#endif
+
+// TODO: Handle system proxy
+#ifndef QT_NO_NETWORKPROXY
+ if (_account.useProxy()) {
+ QNetworkProxy proxy(_account.proxyType(), _account.proxyHostName(), _account.proxyPort(), _account.proxyUser(), _account.proxyPassword());
+ socket->setProxy(proxy);
+ }
+#endif
+
+ setSocket(socket);
+ // handled by the base class for now; may need to rethink for protocol detection
+ //connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(onSocketError(QAbstractSocket::SocketError)));
+ //connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
+ connect(socket, SIGNAL(connected()), SLOT(onSocketConnected()));
+
+ emit statusMessage(tr("Connecting to %1...").arg(_account.accountName()));
+ socket->connectToHost(_account.hostName(), _account.port());
+}
+
+
+// TODO: handle protocol detection
+// This method might go away anyway, unless we really need our own states...
+/*
+void ClientAuthHandler::onSocketStateChanged(QAbstractSocket::SocketState socketState)
+{
+ qDebug() << Q_FUNC_INFO << socketState;
+ QString text;
+ AuthHandler::State state = UnconnectedState;
+
+ switch(socketState) {
+ case QAbstractSocket::UnconnectedState:
+ text = tr("Disconnected");
+ state = UnconnectedState;
+ break;
+ case QAbstractSocket::HostLookupState:
+ text = tr("Looking up %1...").arg(_account.hostName());
+ state = HostLookupState;
+ break;
+ case QAbstractSocket::ConnectingState:
+ text = tr("Connecting to %1...").arg(_account.hostName());
+ state = ConnectingState;
+ break;
+ case QAbstractSocket::ConnectedState:
+ text = tr("Connected to %1").arg(_account.hostName());
+ state = ConnectedState;
+ break;
+ case QAbstractSocket::ClosingState:
+ text = tr("Disconnecting from %1...").arg(_account.hostName());
+ state = ClosingState;
+ break;
+ default:
+ break;
+ }
+
+ if (!text.isEmpty()) {
+ setState(state);
+ emit statusMessage(text);
+ }
+}
+*/
+
+// TODO: handle protocol detection
+/*
+void ClientAuthHandler::onSocketError(QAbstractSocket::SocketError error)
+{
+ emit socketError(error, socket()->errorString());
+}
+*/
+
+void ClientAuthHandler::onSocketConnected()
+{
+ // TODO: protocol detection
+
+ if (_peer) {
+ qWarning() << Q_FUNC_INFO << "Peer already exists!";
+ return;
+ }
+
+ _peer = new LegacyPeer(this, socket(), this);
+
+ connect(_peer, SIGNAL(transferProgress(int,int)), SIGNAL(transferProgress(int,int)));
+
+ // compat only
+ connect(_peer, SIGNAL(protocolVersionMismatch(int,int)), SLOT(onProtocolVersionMismatch(int,int)));
+
+ emit statusMessage(tr("Synchronizing to core..."));
+
+ bool useSsl = false;
+#ifdef HAVE_SSL
+ useSsl = _account.useSsl();
+#endif
+
+ _peer->dispatch(RegisterClient(Quassel::buildInfo().fancyVersionString, useSsl));
+}
+
+
+void ClientAuthHandler::onProtocolVersionMismatch(int actual, int expected)
+{
+ emit errorPopup(tr("<b>The Quassel Core you are trying to connect to is too old!</b><br>"
+ "We need at least protocol v%1, but the core speaks v%2 only.").arg(expected, actual));
+ requestDisconnect();
+}
+
+
+void ClientAuthHandler::handle(const ClientDenied &msg)
+{
+ emit errorPopup(msg.errorString);
+ requestDisconnect();
+}
+
+
+void ClientAuthHandler::handle(const ClientRegistered &msg)
+{
+ _coreConfigured = msg.coreConfigured;
+ _backendInfo = msg.backendInfo;
+
+ Client::setCoreFeatures(static_cast<Quassel::Features>(msg.coreFeatures));
+
+#ifdef HAVE_SSL
+ CoreAccountSettings s;
+ if (_account.useSsl()) {
+ if (msg.sslSupported) {
+ // Make sure the warning is shown next time we don't have SSL in the core
+ s.setAccountValue("ShowNoCoreSslWarning", true);
+
+ QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket());
+ Q_ASSERT(sslSocket);
+ connect(sslSocket, SIGNAL(encrypted()), SLOT(onSslSocketEncrypted()));
+ connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)), SLOT(onSslErrors()));
+ qDebug() << "Starting encryption...";
+ sslSocket->flush();
+ sslSocket->startClientEncryption();
+ }
+ else {
+ if (s.accountValue("ShowNoCoreSslWarning", true).toBool()) {
+ bool accepted = false;
+ emit handleNoSslInCore(&accepted);
+ if (!accepted) {
+ requestDisconnect(tr("Unencrypted connection cancelled"));
+ return;
+ }
+ s.setAccountValue("ShowNoCoreSslWarning", false);
+ s.setAccountValue("SslCert", QString());
+ }
+ onConnectionReady();
+ }
+ return;
+ }
+#endif
+ // if we use SSL we wait for the next step until every SSL warning has been cleared
+ onConnectionReady();
+}
+
+
+#ifdef HAVE_SSL
+
+void ClientAuthHandler::onSslSocketEncrypted()
+{
+ QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
+ Q_ASSERT(socket);
+
+ if (!socket->sslErrors().count()) {
+ // Cert is valid, so we don't want to store it as known
+ // That way, a warning will appear in case it becomes invalid at some point
+ CoreAccountSettings s;
+ s.setAccountValue("SSLCert", QString());
+ }
+
+ emit encrypted(true);
+ onConnectionReady();
+}
+
+
+void ClientAuthHandler::onSslErrors()
+{
+ QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
+ Q_ASSERT(socket);
+
+ CoreAccountSettings s;
+ QByteArray knownDigest = s.accountValue("SslCert").toByteArray();
+
+ if (knownDigest != socket->peerCertificate().digest()) {
+ bool accepted = false;
+ bool permanently = false;
+ emit handleSslErrors(socket, &accepted, &permanently);
+
+ if (!accepted) {
+ requestDisconnect(tr("Unencrypted connection canceled"));
+ return;
+ }
+
+ if (permanently)
+ s.setAccountValue("SslCert", socket->peerCertificate().digest());
+ else
+ s.setAccountValue("SslCert", QString());
+ }
+
+ socket->ignoreSslErrors();
+}
+
+#endif /* HAVE_SSL */
+
+
+void ClientAuthHandler::onConnectionReady()
+{
+ emit connectionReady();
+ emit statusMessage(tr("Connected to %1").arg(_account.accountName()));
+
+ if (!_coreConfigured) {
+ // start wizard
+ emit startCoreSetup(_backendInfo);
+ }
+ else // TODO: check if we need LoginEnabled
+ login();
+}
+
+
+void ClientAuthHandler::setupCore(const SetupData &setupData)
+{
+ _peer->dispatch(setupData);
+}
+
+
+void ClientAuthHandler::handle(const SetupFailed &msg)
+{
+ emit coreSetupFailed(msg.errorString);
+}
+
+
+void ClientAuthHandler::handle(const SetupDone &msg)
+{
+ Q_UNUSED(msg)
+
+ emit coreSetupSuccessful();
+}
+
+
+void ClientAuthHandler::login(const QString &user, const QString &password, bool remember)
+{
+ _account.setUser(user);
+ _account.setPassword(password);
+ _account.setStorePassword(remember);
+ login();
+}
+
+
+void ClientAuthHandler::login(const QString &previousError)
+{
+ emit statusMessage(tr("Logging in..."));
+ if (_account.user().isEmpty() || _account.password().isEmpty() || !previousError.isEmpty()) {
+ bool valid = false;
+ emit userAuthenticationRequired(&_account, &valid, previousError); // *must* be a synchronous call
+ if (!valid || _account.user().isEmpty() || _account.password().isEmpty()) {
+ requestDisconnect(tr("Login canceled"));
+ return;
+ }
+ }
+
+ _peer->dispatch(Login(_account.user(), _account.password()));
+}
+
+
+void ClientAuthHandler::handle(const LoginFailed &msg)
+{
+ login(msg.errorString);
+}
+
+
+void ClientAuthHandler::handle(const LoginSuccess &msg)
+{
+ Q_UNUSED(msg)
+
+ emit loginSuccessful(_account);
+}
+
+
+void ClientAuthHandler::handle(const SessionState &msg)
+{
+ disconnect(socket(), 0, this, 0); // this is the last message we shall ever get
+
+ // give up ownership of the peer; CoreSession takes responsibility now
+ _peer->setParent(0);
+ emit handshakeComplete(_peer, msg);
+}
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-2013 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef CLIENTAUTHHANDLER_H
+#define CLIENTAUTHHANDLER_H
+
+#include "authhandler.h"
+#include "coreaccount.h"
+
+class QSslSocket;
+
+class RemotePeer;
+
+class ClientAuthHandler : public AuthHandler
+{
+ Q_OBJECT
+
+public:
+ ClientAuthHandler(CoreAccount account, QObject *parent = 0);
+
+public slots:
+ void connectToCore();
+
+ void login(const QString &previousError = QString());
+ void login(const QString &user, const QString &password, bool remember);
+ void setupCore(const Protocol::SetupData &setupData);
+
+signals:
+ void statusMessage(const QString &message);
+ void errorMessage(const QString &message);
+ void errorPopup(const QString &message);
+ void transferProgress(int current, int max);
+
+ void requestDisconnect(const QString &errorString = QString(), bool wantReconnect = false);
+
+ void connectionReady();
+ void loginSuccessful(const CoreAccount &account);
+ void handshakeComplete(RemotePeer *peer, const Protocol::SessionState &sessionState);
+
+ // These signals MUST be handled synchronously!
+ void userAuthenticationRequired(CoreAccount *account, bool *valid, const QString &errorMessage = QString());
+ void handleNoSslInClient(bool *accepted);
+ void handleNoSslInCore(bool *accepted);
+#ifdef HAVE_SSL
+ void handleSslErrors(const QSslSocket *socket, bool *accepted, bool *permanently);
+#endif
+
+ void encrypted(bool isEncrypted = true);
+ void startCoreSetup(const QVariantList &backendInfo);
+ void coreSetupSuccessful();
+ void coreSetupFailed(const QString &error);
+
+private:
+ using AuthHandler::handle;
+
+ void handle(const Protocol::ClientDenied &msg);
+ void handle(const Protocol::ClientRegistered &msg);
+ void handle(const Protocol::SetupFailed &msg);
+ void handle(const Protocol::SetupDone &msg);
+ void handle(const Protocol::LoginFailed &msg);
+ void handle(const Protocol::LoginSuccess &msg);
+ void handle(const Protocol::SessionState &msg);
+
+private slots:
+ void onSocketConnected();
+ //void onSocketStateChanged(QAbstractSocket::SocketState state);
+ //void onSocketError(QAbstractSocket::SocketError);
+#ifdef HAVE_SSL
+ void onSslSocketEncrypted();
+ void onSslErrors();
+#endif
+
+ void onProtocolVersionMismatch(int actual, int expected);
+
+ void onConnectionReady();
+
+private:
+ RemotePeer *_peer;
+ bool _coreConfigured;
+ QVariantList _backendInfo;
+ CoreAccount _account;
+
+};
+
+#endif
void setProxyHostName(const QString &);
void setProxyPort(uint);
- /* These might be overridden for KWallet support */
+ /* These might be overridden for KWallet/QtKeychain support */
virtual inline QString password() const { return _password; }
virtual void setPassword(const QString &password);
virtual inline QString proxyPassword() const { return _proxyPassword; }
inline AccountId internalAccount() const;
- AccountId createOrUpdateAccount(const CoreAccount &newAccountData = CoreAccount());
+ AccountId createOrUpdateAccount(const CoreAccount &newAccountData);
CoreAccount takeAccount(AccountId);
void removeAccount(AccountId);
#include "coreconnection.h"
-#ifndef QT_NO_NETWORKPROXY
-# include <QNetworkProxy>
-#endif
-
#include "client.h"
+#include "clientauthhandler.h"
#include "clientsettings.h"
#include "coreaccountmodel.h"
#include "identity.h"
#include "protocols/legacy/legacypeer.h"
-CoreConnection::CoreConnection(CoreAccountModel *model, QObject *parent)
+CoreConnection::CoreConnection(QObject *parent)
: QObject(parent),
- _model(model),
+ _authHandler(0),
_state(Disconnected),
_wantReconnect(false),
_wasReconnect(false),
}
+CoreAccountModel *CoreConnection::accountModel() const
+{
+ return Client::coreAccountModel();
+}
+
+
void CoreConnection::setProgressText(const QString &text)
{
if (_progressText != text) {
}
}
-
#endif
bool CoreConnection::isEncrypted() const
}
+void CoreConnection::onConnectionReady()
+{
+ setState(Connected);
+}
+
+
void CoreConnection::setState(ConnectionState state)
{
if (state != _state) {
}
-void CoreConnection::coreSocketError(QAbstractSocket::SocketError)
+void CoreConnection::coreSocketError(QAbstractSocket::SocketError error, const QString &errorString)
{
- disconnectFromCore(_socket->errorString(), true);
+ Q_UNUSED(error)
+
+ disconnectFromCore(errorString, true);
}
}
-// note: this still expects the legacy protocol
-// note²: after cleaning this up, we can probably get rid of _socket altogether
-void CoreConnection::coreHasData(const QVariant &item)
-{
- QVariantMap msg = item.toMap();
- if (!msg.contains("MsgType")) {
- // This core is way too old and does not even speak our init protocol...
- emit connectionErrorPopup(tr("The Quassel Core you try to connect to is too old! Please consider upgrading."));
- disconnectFromCore(QString(), false);
- return;
- }
- if (msg["MsgType"] == "ClientInitAck") {
- clientInitAck(msg);
- }
- else if (msg["MsgType"] == "ClientInitReject") {
- emit connectionErrorPopup(msg["Error"].toString());
- disconnectFromCore(QString(), false);
- return;
- }
- else if (msg["MsgType"] == "CoreSetupAck") {
- emit coreSetupSuccess();
- }
- else if (msg["MsgType"] == "CoreSetupReject") {
- emit coreSetupFailed(msg["Error"].toString());
- }
- else if (msg["MsgType"] == "ClientLoginReject") {
- loginFailed(msg["Error"].toString());
- }
- else if (msg["MsgType"] == "ClientLoginAck") {
- loginSuccess();
- }
- else if (msg["MsgType"] == "SessionInit") {
- // that's it, let's hand over to the signal proxy
- // if the connection is an orphan, the signalProxy adopts it.
- // -> we don't need to care about it anymore
-
- disconnect(_peer, 0, this, 0);
-
- _peer->setParent(0);
- Client::signalProxy()->addPeer(_peer);
-
- sessionStateReceived(msg["SessionState"].toMap());
- }
- else {
- disconnectFromCore(tr("Invalid data received from core"), false);
- return;
- }
-}
-
-
void CoreConnection::disconnectFromCore()
{
- if (_socket)
- disconnectFromCore(QString(), false); // requested disconnect, so don't try to reconnect
+ disconnectFromCore(QString(), false); // requested disconnect, so don't try to reconnect
}
_wantReconnect = wantReconnect; // store if disconnect was requested
_wasReconnect = false;
- if (_socket)
- _socket->close();
+ if (_authHandler)
+ _authHandler->close();
+ else if(_peer)
+ _peer->close();
if (errorString.isEmpty())
emit connectionError(tr("Disconnected"));
_wantReconnect = wantReconnect;
+ if (_authHandler) {
+ disconnect(_authHandler, 0, this, 0);
+ _authHandler->close();
+ _authHandler->deleteLater();
+ _authHandler = 0;
+ }
+
if (_peer) {
- disconnect(_socket, 0, this, 0);
disconnect(_peer, 0, this, 0);
+ // peer belongs to the sigproxy and thus gets deleted by it
_peer->close();
-
- if (_peer->parent() == this)
- _peer->deleteLater(); // if it's not us, it belongs to the sigproxy which will delete it
- _socket = 0; // socket is owned and will be deleted by RemoteConnection
_peer = 0;
}
- else if (_socket) {
- disconnect(_socket, 0, this, 0);
- _socket->deleteLater();
- _socket = 0;
- }
- _coreMsgBuffer.clear();
_netsToSync.clear();
_numNetsToSync = 0;
void CoreConnection::connectToCurrentAccount()
{
+ if (_authHandler) {
+ qWarning() << Q_FUNC_INFO << "Already connected!";
+ return;
+ }
+
resetConnection(false);
if (currentAccount().isInternal()) {
emit startInternalCore();
InternalPeer *peer = new InternalPeer();
+ _peer = peer;
Client::instance()->signalProxy()->addPeer(peer); // sigproxy will take ownership
emit connectToInternalCore(peer);
return;
}
- CoreAccountSettings s;
-
- Q_ASSERT(!_socket);
-#ifdef HAVE_SSL
- QSslSocket *sock = new QSslSocket(this);
- // make sure the warning is shown if we happen to connect without SSL support later
- s.setAccountValue("ShowNoClientSslWarning", true);
-#else
- if (_account.useSsl()) {
- if (s.accountValue("ShowNoClientSslWarning", true).toBool()) {
- bool accepted = false;
- emit handleNoSslInClient(&accepted);
- if (!accepted) {
- emit connectionError(tr("Unencrypted connection canceled"));
- return;
- }
- s.setAccountValue("ShowNoClientSslWarning", false);
- }
- }
- QTcpSocket *sock = new QTcpSocket(this);
-#endif
-
-#ifndef QT_NO_NETWORKPROXY
- if (_account.useProxy()) {
- QNetworkProxy proxy(_account.proxyType(), _account.proxyHostName(), _account.proxyPort(), _account.proxyUser(), _account.proxyPassword());
- sock->setProxy(proxy);
- }
-#endif
-
- _socket = sock;
- connect(sock, SIGNAL(connected()), SLOT(coreSocketConnected()));
- connect(sock, SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
- connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(coreSocketError(QAbstractSocket::SocketError)));
- connect(sock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SLOT(socketStateChanged(QAbstractSocket::SocketState)));
-
- emit connectionMsg(tr("Connecting to %1...").arg(currentAccount().accountName()));
- sock->connectToHost(_account.hostName(), _account.port());
-}
-
-
-void CoreConnection::coreSocketConnected()
-{
- // Create the connection which will handle the incoming data
- Q_ASSERT(!_peer);
- _peer = new LegacyPeer(_socket, this);
- connect(_peer, SIGNAL(dataReceived(QVariant)), SLOT(coreHasData(QVariant)));
- connect(_peer, SIGNAL(transferProgress(int,int)), SLOT(updateProgress(int,int)));
-
- // Phase One: Send client info and wait for core info
-
- emit connectionMsg(tr("Synchronizing to core..."));
-
- QVariantMap clientInit;
- clientInit["MsgType"] = "ClientInit";
- clientInit["ClientVersion"] = Quassel::buildInfo().fancyVersionString;
- clientInit["ClientDate"] = Quassel::buildInfo().buildDate;
- clientInit["ProtocolVersion"] = Quassel::buildInfo().protocolVersion;
- clientInit["UseSsl"] = _account.useSsl();
-#ifndef QT_NO_COMPRESS
- clientInit["UseCompression"] = true;
-#else
- clientInit["UseCompression"] = false;
-#endif
-
- qobject_cast<RemotePeer *>(_peer)->writeSocketData(clientInit);
-}
-
-
-void CoreConnection::clientInitAck(const QVariantMap &msg)
-{
- // Core has accepted our version info and sent its own. Let's see if we accept it as well...
- uint ver = msg["ProtocolVersion"].toUInt();
- if (ver < Quassel::buildInfo().clientNeedsProtocol) {
- emit connectionErrorPopup(tr("<b>The Quassel Core you are trying to connect to is too old!</b><br>"
- "Need at least core/client protocol v%1 to connect.").arg(Quassel::buildInfo().clientNeedsProtocol));
- disconnectFromCore(QString(), false);
- return;
- }
-
- Client::setCoreFeatures((Quassel::Features)msg["CoreFeatures"].toUInt());
-
-#ifndef QT_NO_COMPRESS
- if (msg["SupportsCompression"].toBool()) {
- _socket->setProperty("UseCompression", true);
- }
-#endif
-
- _coreMsgBuffer = msg;
-
+ _authHandler = new ClientAuthHandler(currentAccount(), this);
+
+ connect(_authHandler, SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
+ connect(_authHandler, SIGNAL(connectionReady()), SLOT(onConnectionReady()));
+ connect(_authHandler, SIGNAL(socketStateChanged(QAbstractSocket::SocketState)), SLOT(socketStateChanged(QAbstractSocket::SocketState)));
+ connect(_authHandler, SIGNAL(socketError(QAbstractSocket::SocketError,QString)), SLOT(coreSocketError(QAbstractSocket::SocketError,QString)));
+ connect(_authHandler, SIGNAL(transferProgress(int,int)), SLOT(updateProgress(int,int)));
+ connect(_authHandler, SIGNAL(requestDisconnect(QString,bool)), SLOT(disconnectFromCore(QString,bool)));
+
+ connect(_authHandler, SIGNAL(errorMessage(QString)), SIGNAL(connectionError(QString)));
+ connect(_authHandler, SIGNAL(errorPopup(QString)), SIGNAL(connectionErrorPopup(QString)));
+ connect(_authHandler, SIGNAL(statusMessage(QString)), SIGNAL(connectionMsg(QString)));
+ connect(_authHandler, SIGNAL(encrypted(bool)), SIGNAL(encrypted(bool)));
+ connect(_authHandler, SIGNAL(startCoreSetup(QVariantList)), SIGNAL(startCoreSetup(QVariantList)));
+ connect(_authHandler, SIGNAL(coreSetupFailed(QString)), SIGNAL(coreSetupFailed(QString)));
+ connect(_authHandler, SIGNAL(coreSetupSuccessful()), SIGNAL(coreSetupSuccess()));
+ connect(_authHandler, SIGNAL(userAuthenticationRequired(CoreAccount*,bool*,QString)), SIGNAL(userAuthenticationRequired(CoreAccount*,bool*,QString)));
+ connect(_authHandler, SIGNAL(handleNoSslInClient(bool*)), SIGNAL(handleNoSslInClient(bool*)));
+ connect(_authHandler, SIGNAL(handleNoSslInCore(bool*)), SIGNAL(handleNoSslInCore(bool*)));
#ifdef HAVE_SSL
- CoreAccountSettings s;
- if (currentAccount().useSsl()) {
- if (msg["SupportSsl"].toBool()) {
- // Make sure the warning is shown next time we don't have SSL in the core
- s.setAccountValue("ShowNoCoreSslWarning", true);
-
- QSslSocket *sslSocket = qobject_cast<QSslSocket *>(_socket);
- Q_ASSERT(sslSocket);
- connect(sslSocket, SIGNAL(encrypted()), SLOT(sslSocketEncrypted()));
- connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError> &)), SLOT(sslErrors()));
- sslSocket->startClientEncryption();
- }
- else {
- if (s.accountValue("ShowNoCoreSslWarning", true).toBool()) {
- bool accepted = false;
- emit handleNoSslInCore(&accepted);
- if (!accepted) {
- disconnectFromCore(tr("Unencrypted connection canceled"), false);
- return;
- }
- s.setAccountValue("ShowNoCoreSslWarning", false);
- s.setAccountValue("SslCert", QString());
- }
- connectionReady();
- }
- return;
- }
+ connect(_authHandler, SIGNAL(handleSslErrors(const QSslSocket*,bool*,bool*)), SIGNAL(handleSslErrors(const QSslSocket*,bool*,bool*)));
#endif
- // if we use SSL we wait for the next step until every SSL warning has been cleared
- connectionReady();
-}
-
-
-#ifdef HAVE_SSL
-
-void CoreConnection::sslSocketEncrypted()
-{
- QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
- Q_ASSERT(socket);
- if (!socket->sslErrors().count()) {
- // Cert is valid, so we don't want to store it as known
- // That way, a warning will appear in case it becomes invalid at some point
- CoreAccountSettings s;
- s.setAccountValue("SSLCert", QString());
- }
+ connect(_authHandler, SIGNAL(loginSuccessful(CoreAccount)), SLOT(onLoginSuccessful(CoreAccount)));
+ connect(_authHandler, SIGNAL(handshakeComplete(RemotePeer*,Protocol::SessionState)), SLOT(onHandshakeComplete(RemotePeer*,Protocol::SessionState)));
- emit encrypted(true);
- connectionReady();
+ _authHandler->connectToCore();
}
-void CoreConnection::sslErrors()
+void CoreConnection::setupCore(const Protocol::SetupData &setupData)
{
- QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
- Q_ASSERT(socket);
-
- CoreAccountSettings s;
- QByteArray knownDigest = s.accountValue("SslCert").toByteArray();
-
- if (knownDigest != socket->peerCertificate().digest()) {
- bool accepted = false;
- bool permanently = false;
- emit handleSslErrors(socket, &accepted, &permanently);
-
- if (!accepted) {
- disconnectFromCore(tr("Unencrypted connection canceled"), false);
- return;
- }
-
- if (permanently)
- s.setAccountValue("SslCert", socket->peerCertificate().digest());
- else
- s.setAccountValue("SslCert", QString());
- }
-
- socket->ignoreSslErrors();
-}
-
-
-#endif /* HAVE_SSL */
-
-void CoreConnection::connectionReady()
-{
- setState(Connected);
- emit connectionMsg(tr("Connected to %1").arg(currentAccount().accountName()));
-
- if (!_coreMsgBuffer["Configured"].toBool()) {
- // start wizard
- emit startCoreSetup(_coreMsgBuffer["StorageBackends"].toList());
- }
- else if (_coreMsgBuffer["LoginEnabled"].toBool()) {
- loginToCore();
- }
- _coreMsgBuffer.clear();
+ _authHandler->setupCore(setupData);
}
void CoreConnection::loginToCore(const QString &user, const QString &password, bool remember)
{
- _account.setUser(user);
- _account.setPassword(password);
- _account.setStorePassword(remember);
- loginToCore();
-}
-
-
-void CoreConnection::loginToCore(const QString &prevError)
-{
- emit connectionMsg(tr("Logging in..."));
- if (currentAccount().user().isEmpty() || currentAccount().password().isEmpty() || !prevError.isEmpty()) {
- bool valid = false;
- emit userAuthenticationRequired(&_account, &valid, prevError); // *must* be a synchronous call
- if (!valid || currentAccount().user().isEmpty() || currentAccount().password().isEmpty()) {
- disconnectFromCore(tr("Login canceled"), false);
- return;
- }
- }
-
- QVariantMap clientLogin;
- clientLogin["MsgType"] = "ClientLogin";
- clientLogin["User"] = currentAccount().user();
- clientLogin["Password"] = currentAccount().password();
- qobject_cast<RemotePeer*>(_peer)->writeSocketData(clientLogin);
-}
-
-
-void CoreConnection::loginFailed(const QString &error)
-{
- loginToCore(error);
+ _authHandler->login(user, password, remember);
}
-void CoreConnection::loginSuccess()
+void CoreConnection::onLoginSuccessful(const CoreAccount &account)
{
updateProgress(0, 0);
// save current account data
- _model->createOrUpdateAccount(currentAccount());
- _model->save();
+ accountModel()->createOrUpdateAccount(account);
+ accountModel()->save();
_reconnectTimer.stop();
setProgressText(tr("Receiving session state"));
setState(Synchronizing);
- emit connectionMsg(tr("Synchronizing to %1...").arg(currentAccount().accountName()));
+ emit connectionMsg(tr("Synchronizing to %1...").arg(account.accountName()));
}
-void CoreConnection::sessionStateReceived(const QVariantMap &state)
+void CoreConnection::onHandshakeComplete(RemotePeer *peer, const Protocol::SessionState &sessionState)
{
updateProgress(100, 100);
- syncToCore(state);
+ disconnect(_authHandler, 0, this, 0);
+ _authHandler->deleteLater();
+ _authHandler = 0;
+
+ _peer = peer;
+ connect(peer, SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
+ connect(peer, SIGNAL(socketStateChanged(QAbstractSocket::SocketState)), SLOT(socketStateChanged(QAbstractSocket::SocketState)));
+ connect(peer, SIGNAL(socketError(QAbstractSocket::SocketError,QString)), SLOT(coreSocketError(QAbstractSocket::SocketError,QString)));
+
+ Client::signalProxy()->addPeer(_peer); // sigproxy takes ownership of the peer!
+
+ syncToCore(sessionState);
}
-void CoreConnection::internalSessionStateReceived(const QVariant &packedState)
+void CoreConnection::internalSessionStateReceived(const Protocol::SessionState &sessionState)
{
updateProgress(100, 100);
+ Client::setCoreFeatures(Quassel::features()); // mono connection...
+
setState(Synchronizing);
- syncToCore(packedState.toMap());
+ syncToCore(sessionState);
}
-void CoreConnection::syncToCore(const QVariantMap &sessionState)
+void CoreConnection::syncToCore(const Protocol::SessionState &sessionState)
{
- if (sessionState.contains("CoreFeatures"))
- Client::setCoreFeatures((Quassel::Features)sessionState["CoreFeatures"].toUInt());
-
setProgressText(tr("Receiving network states"));
updateProgress(0, 100);
// create identities
- foreach(QVariant vid, sessionState["Identities"].toList()) {
+ foreach(const QVariant &vid, sessionState.identities) {
Client::instance()->coreIdentityCreated(vid.value<Identity>());
}
// create buffers
// FIXME: get rid of this crap -- why?
- QVariantList bufferinfos = sessionState["BufferInfos"].toList();
NetworkModel *networkModel = Client::networkModel();
Q_ASSERT(networkModel);
- foreach(QVariant vinfo, bufferinfos)
- networkModel->bufferUpdated(vinfo.value<BufferInfo>()); // create BufferItems
-
- QVariantList networkids = sessionState["NetworkIds"].toList();
+ foreach(const QVariant &vinfo, sessionState.bufferInfos)
+ networkModel->bufferUpdated(vinfo.value<BufferInfo>()); // create BufferItems
// prepare sync progress thingys...
// FIXME: Care about removal of networks
- _numNetsToSync = networkids.count();
+ _numNetsToSync = sessionState.networkIds.count();
updateProgress(0, _numNetsToSync);
// create network objects
- foreach(QVariant networkid, networkids) {
+ foreach(const QVariant &networkid, sessionState.networkIds) {
NetworkId netid = networkid.value<NetworkId>();
if (Client::network(netid))
continue;
emit synchronized();
}
}
-
-
-void CoreConnection::doCoreSetup(const QVariant &setupData)
-{
- QVariantMap setup;
- setup["MsgType"] = "CoreSetupData";
- setup["SetupData"] = setupData;
- qobject_cast<RemotePeer *>(_peer)->writeSocketData(setup);
-}
#ifndef CORECONNECTION_H_
#define CORECONNECTION_H_
-// TODO: support system application proxy (new in Qt 4.6)
-
#include "QPointer"
#include "QTimer"
#include "remotepeer.h"
#include "types.h"
+class ClientAuthHandler;
class CoreAccountModel;
class InternalPeer;
class Network;
Synchronized
};
- CoreConnection(CoreAccountModel *model, QObject *parent = 0);
+ CoreConnection(QObject *parent = 0);
void init();
//! Check if we consider the last connect as reconnect
bool wasReconnect() const { return _wasReconnect; }
-#ifdef HAVE_SSL
- const QSslSocket *sslSocket() const;
-#endif
-
public slots:
bool connectToCore(AccountId = 0);
void reconnectToCore();
void disconnectFromCore();
+ void setupCore(const Protocol::SetupData &setupData);
+
signals:
void stateChanged(CoreConnection::ConnectionState);
void encrypted(bool isEncrypted = true);
void connectionError(const QString &errorMsg);
void connectionErrorPopup(const QString &errorMsg);
- void connectionWarnings(const QStringList &warnings);
void connectionMsg(const QString &msg);
void disconnected();
void progressValueChanged(int value);
void progressTextChanged(const QString &);
- void startCoreSetup(const QVariantList &);
+ void startCoreSetup(const QVariantList &backendInfo);
void coreSetupSuccess();
void coreSetupFailed(const QString &error);
void disconnectFromCore(const QString &errorString, bool wantReconnect = true);
void socketStateChanged(QAbstractSocket::SocketState);
- void coreSocketError(QAbstractSocket::SocketError);
- void coreHasData(const QVariant &item);
- void coreSocketConnected();
+ void coreSocketError(QAbstractSocket::SocketError error, const QString &errorString);
void coreSocketDisconnected();
- void clientInitAck(const QVariantMap &msg);
-
// for sync progress
void networkInitDone();
void checkSyncState();
- void syncToCore(const QVariantMap &sessionState);
- void internalSessionStateReceived(const QVariant &packedState);
- void sessionStateReceived(const QVariantMap &state);
+ void loginToCore(const QString &user, const QString &password, bool remember); // for config wizard
+ void syncToCore(const Protocol::SessionState &sessionState);
+ void internalSessionStateReceived(const Protocol::SessionState &sessionState);
void resetConnection(bool wantReconnect = false);
- void connectionReady();
-
- void loginToCore(const QString &user, const QString &password, bool remember); // for config wizard
- void loginToCore(const QString &previousError = QString());
- void loginSuccess();
- void loginFailed(const QString &errorMessage);
- void doCoreSetup(const QVariant &setupData);
+ void onConnectionReady();
+ void onLoginSuccessful(const CoreAccount &account);
+ void onHandshakeComplete(RemotePeer *peer, const Protocol::SessionState &sessionState);
void updateProgress(int value, int maximum);
void setProgressText(const QString &text);
void setState(QAbstractSocket::SocketState socketState);
void setState(ConnectionState state);
-#ifdef HAVE_SSL
- void sslSocketEncrypted();
- void sslErrors();
-#endif
-
void networkDetectionModeChanged(const QVariant &mode);
void pingTimeoutIntervalChanged(const QVariant &interval);
void reconnectIntervalChanged(const QVariant &interval);
#endif
private:
- CoreAccountModel *_model;
- CoreAccount _account;
- QVariantMap _coreMsgBuffer;
-
- QPointer<QTcpSocket> _socket;
+ QPointer<ClientAuthHandler> _authHandler;
QPointer<Peer> _peer;
ConnectionState _state;
int _progressMinimum, _progressMaximum, _progressValue;
QString _progressText;
- QString _coreInfoString(const QVariantMap &);
bool _resetting;
- inline CoreAccountModel *accountModel() const;
+ CoreAccount _account;
+ CoreAccountModel *accountModel() const;
friend class CoreConfigWizard;
};
inline CoreConnection::ConnectionState CoreConnection::state() const { return _state; }
inline bool CoreConnection::isConnected() const { return state() >= Connected; }
inline CoreAccount CoreConnection::currentAccount() const { return _account; }
-inline CoreAccountModel *CoreConnection::accountModel() const { return _model; }
-
-#ifdef HAVE_SSL
-inline const QSslSocket *CoreConnection::sslSocket() const { return qobject_cast<QSslSocket *>(_socket); }
-#endif
#endif
set(SOURCES
aliasmanager.cpp
+ authhandler.cpp
backlogmanager.cpp
basichandler.cpp
bufferinfo.cpp
set(MOC_HDRS
aliasmanager.h
+ authhandler.h
backlogmanager.h
basichandler.h
buffersyncer.h
set(SOURCES ${SOURCES} logbacktrace_unix.cpp)
endif(CMAKE_HOST_UNIX)
-include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_SOURCE_DIR} ${QUASSEL_QT_INCLUDES}) # for version.inc and version.gen
+include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${QUASSEL_QT_INCLUDES}) # for version.inc and version.gen
if(NOT WITH_QT5)
qt4_wrap_cpp(MOC ${MOC_HDRS})
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-2013 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "authhandler.h"
+
+AuthHandler::AuthHandler(QObject *parent)
+ : QObject(parent),
+ _state(UnconnectedState),
+ _socket(0)
+{
+
+}
+
+
+AuthHandler::State AuthHandler::state() const
+{
+ return _state;
+}
+
+
+void AuthHandler::setState(AuthHandler::State state)
+{
+ if (_state != state) {
+ _state = state;
+ emit stateChanged(state);
+ }
+}
+
+
+QTcpSocket *AuthHandler::socket() const
+{
+ return _socket;
+}
+
+
+void AuthHandler::setSocket(QTcpSocket *socket)
+{
+ _socket = socket;
+ connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SIGNAL(socketStateChanged(QAbstractSocket::SocketState)));
+ connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError)));
+ connect(socket, SIGNAL(disconnected()), SIGNAL(disconnected()));
+}
+
+
+void AuthHandler::socketError(QAbstractSocket::SocketError error)
+{
+ emit socketError(error, _socket->errorString());
+}
+
+
+void AuthHandler::invalidMessage()
+{
+ qWarning() << Q_FUNC_INFO << "No handler for message!";
+}
+
+
+void AuthHandler::close()
+{
+ if (_socket && _socket->isOpen())
+ _socket->close();
+}
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-2013 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef AUTHHANDLER_H
+#define AUTHHANDLER_H
+
+#include <QTcpSocket>
+
+#include "protocol.h"
+
+class Peer;
+
+class AuthHandler : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum State {
+ UnconnectedState,
+ HostLookupState,
+ ConnectingState,
+ ConnectedState,
+ RetryWithLegacyState,
+ AuthenticatingState,
+ AuthenticatedState,
+ ClosingState
+ };
+
+ AuthHandler(QObject *parent = 0);
+
+ State state() const;
+ QTcpSocket *socket() const;
+
+ virtual void handle(const Protocol::RegisterClient &) { invalidMessage(); }
+ virtual void handle(const Protocol::ClientDenied &) { invalidMessage(); }
+ virtual void handle(const Protocol::ClientRegistered &) { invalidMessage(); }
+ virtual void handle(const Protocol::SetupData &) { invalidMessage(); }
+ virtual void handle(const Protocol::SetupFailed &) { invalidMessage(); }
+ virtual void handle(const Protocol::SetupDone &) { invalidMessage(); }
+ virtual void handle(const Protocol::Login &) { invalidMessage(); }
+ virtual void handle(const Protocol::LoginFailed &) { invalidMessage(); }
+ virtual void handle(const Protocol::LoginSuccess &) { invalidMessage(); }
+ virtual void handle(const Protocol::SessionState &) { invalidMessage(); }
+
+ // fallback for unknown types, will trigger an error
+ template<class T>
+ void handle(const T &) { invalidMessage(); }
+
+public slots:
+ void close();
+
+signals:
+ void stateChanged(State state);
+ void disconnected();
+
+ void socketStateChanged(QAbstractSocket::SocketState state);
+ void socketError(QAbstractSocket::SocketError error, const QString &errorString);
+
+protected:
+ void setSocket(QTcpSocket *socket);
+ void setState(State state);
+
+private slots:
+ void socketError(QAbstractSocket::SocketError error);
+
+private:
+ void invalidMessage();
+
+ State _state;
+ QTcpSocket *_socket; // FIXME: should be a QSharedPointer? -> premature disconnect before the peer has taken over
+};
+
+#endif
InternalPeer::InternalPeer(QObject *parent)
- : Peer(parent),
+ : Peer(0, parent),
_proxy(0),
_peer(0),
_isOpen(true)
void dispatch(const Protocol::InitRequest &msg);
void dispatch(const Protocol::InitData &msg);
+ /* These are not needed for InternalPeer */
+ void dispatch(const Protocol::RegisterClient &) {}
+ void dispatch(const Protocol::ClientDenied &) {}
+ void dispatch(const Protocol::ClientRegistered &) {}
+ void dispatch(const Protocol::SetupData &) {}
+ void dispatch(const Protocol::SetupFailed &) {}
+ void dispatch(const Protocol::SetupDone &) {}
+ void dispatch(const Protocol::Login &) {}
+ void dispatch(const Protocol::LoginFailed &) {}
+ void dispatch(const Protocol::LoginSuccess &) {}
+ void dispatch(const Protocol::SessionState &) {}
+
public slots:
void close(const QString &reason = QString());
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
+
+#include "peer.h"
+
+Peer::Peer(AuthHandler *authHandler, QObject *parent)
+ : QObject(parent)
+ , _authHandler(authHandler)
+{
+
+}
+
+
+AuthHandler *Peer::authHandler() const
+{
+ return _authHandler;
+}
#define PEER_H
#include <QAbstractSocket>
+#include <QPointer>
+#include "authhandler.h"
#include "protocol.h"
#include "signalproxy.h"
Q_OBJECT
public:
- Peer(QObject *parent = 0) : QObject(parent) {}
+ Peer(AuthHandler *authHandler, QObject *parent = 0);
virtual QString description() const = 0;
virtual SignalProxy *signalProxy() const = 0;
virtual void setSignalProxy(SignalProxy *proxy) = 0;
+ AuthHandler *authHandler() const;
+
virtual bool isOpen() const = 0;
virtual bool isSecure() const = 0;
virtual bool isLocal() const = 0;
virtual int lag() const = 0;
public slots:
- virtual void dispatch(const Protocol::SyncMessage &msg) = 0;
- virtual void dispatch(const Protocol::RpcCall &msg) = 0;
- virtual void dispatch(const Protocol::InitRequest &msg) = 0;
- virtual void dispatch(const Protocol::InitData &msg) = 0;
+ /* Handshake messages */
+ virtual void dispatch(const Protocol::RegisterClient &) = 0;
+ virtual void dispatch(const Protocol::ClientDenied &) = 0;
+ virtual void dispatch(const Protocol::ClientRegistered &) = 0;
+ virtual void dispatch(const Protocol::SetupData &) = 0;
+ virtual void dispatch(const Protocol::SetupFailed &) = 0;
+ virtual void dispatch(const Protocol::SetupDone &) = 0;
+ virtual void dispatch(const Protocol::Login &) = 0;
+ virtual void dispatch(const Protocol::LoginFailed &) = 0;
+ virtual void dispatch(const Protocol::LoginSuccess &) = 0;
+ virtual void dispatch(const Protocol::SessionState &) = 0;
+
+ /* Sigproxy messages */
+ virtual void dispatch(const Protocol::SyncMessage &) = 0;
+ virtual void dispatch(const Protocol::RpcCall &) = 0;
+ virtual void dispatch(const Protocol::InitRequest &) = 0;
+ virtual void dispatch(const Protocol::InitData &) = 0;
virtual void close(const QString &reason = QString()) = 0;
signals:
void disconnected();
- void error(QAbstractSocket::SocketError);
void secureStateChanged(bool secure = true);
void lagUpdated(int msecs);
protected:
- template<class T>
+ template<typename T>
void handle(const T &protoMessage);
+
+private:
+ QPointer<AuthHandler> _authHandler;
};
// Template method needed in the header
-template<class T> inline
+template<typename T> inline
void Peer::handle(const T &protoMessage)
{
switch(protoMessage.handler()) {
signalProxy()->handle(this, protoMessage);
break;
+ case Protocol::AuthHandler:
+ if (!authHandler()) {
+ qWarning() << Q_FUNC_INFO << "Cannot handle auth messages without an active AuthHandler!";
+ return;
+ }
+ authHandler()->handle(protoMessage);
+ break;
+
default:
qWarning() << Q_FUNC_INFO << "Unknown handler for protocol message!";
return;
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
+#include <QHostAddress>
#include <QTcpSocket>
#include "legacypeer.h"
+#include "quassel.h"
+
+/* version.inc is no longer used for this */
+const int protocolVersion = 10;
+const int coreNeedsProtocol = protocolVersion;
+const int clientNeedsProtocol = protocolVersion;
using namespace Protocol;
-LegacyPeer::LegacyPeer(QTcpSocket *socket, QObject *parent)
- : RemotePeer(socket, parent),
+LegacyPeer::LegacyPeer(::AuthHandler *authHandler, QTcpSocket *socket, QObject *parent)
+ : RemotePeer(authHandler, socket, parent),
_blockSize(0),
_useCompression(false)
{
{
RemotePeer::setSignalProxy(proxy);
+ // FIXME only in compat mode
+ socket()->flush();
if (proxy) {
// enable compression now if requested - the initial handshake is uncompressed in the legacy protocol!
_useCompression = socket()->property("UseCompression").toBool();
+ if (_useCompression)
+ qDebug() << "Using compression for peer:" << qPrintable(socket()->peerAddress().toString());
}
}
while (readSocketData(item)) {
// if no sigproxy is set, we're in handshake mode and let the data be handled elsewhere
if (!signalProxy())
- emit dataReceived(item);
+ handleHandshakeMessage(item);
else
handlePackedFunc(item);
}
}
+/*** Handshake messages ***/
+
+/* These messages are transmitted during handshake phase, which in case of the legacy protocol means they have
+ * a structure different from those being used after the handshake.
+ * Also, the legacy handshake does not fully match the redesigned one, so we'll have to do various mappings here.
+ */
+
+void LegacyPeer::handleHandshakeMessage(const QVariant &msg)
+{
+ QVariantMap m = msg.toMap();
+
+ QString msgType = m["MsgType"].toString();
+ if (msgType.isEmpty()) {
+ emit protocolError(tr("Invalid handshake message!"));
+ return;
+ }
+
+ if (msgType == "ClientInit") {
+ // FIXME only in compat mode
+ int ver = m["ProtocolVersion"].toInt();
+ if (ver < coreNeedsProtocol) {
+ emit protocolVersionMismatch(ver, coreNeedsProtocol);
+ return;
+ }
+
+#ifndef QT_NO_COMPRESS
+ // FIXME only in compat mode
+ if (m["UseCompression"].toBool()) {
+ socket()->setProperty("UseCompression", true);
+ }
+#endif
+ handle(RegisterClient(m["ClientVersion"].toString(), m["UseSsl"].toBool()));
+ }
+
+ else if (msgType == "ClientInitReject") {
+ handle(ClientDenied(m["Error"].toString()));
+ }
+
+ else if (msgType == "ClientInitAck") {
+ // FIXME only in compat mode
+ int ver = m["ProtocolVersion"].toInt();
+ if (ver < clientNeedsProtocol) {
+ emit protocolVersionMismatch(ver, clientNeedsProtocol);
+ return;
+ }
+#ifndef QT_NO_COMPRESS
+ if (m["SupportsCompression"].toBool())
+ socket()->setProperty("UseCompression", true);
+#endif
+
+ handle(ClientRegistered(m["CoreFeatures"].toUInt(), m["Configured"].toBool(), m["StorageBackends"].toList(), m["SupportSsl"].toBool(), QDateTime()));
+ }
+
+ else if (msgType == "CoreSetupData") {
+ QVariantMap map = m["SetupData"].toMap();
+ handle(SetupData(map["AdminUser"].toString(), map["AdminPasswd"].toString(), map["Backend"].toString(), map["ConnectionProperties"].toMap()));
+ }
+
+ else if (msgType == "CoreSetupReject") {
+ handle(SetupFailed(m["Error"].toString()));
+ }
+
+ else if (msgType == "CoreSetupAck") {
+ handle(SetupDone());
+ }
+
+ else if (msgType == "ClientLogin") {
+ handle(Login(m["User"].toString(), m["Password"].toString()));
+ }
+
+ else if (msgType == "ClientLoginReject") {
+ handle(LoginFailed(m["Error"].toString()));
+ }
+
+ else if (msgType == "ClientLoginAck") {
+ handle(LoginSuccess());
+ }
+
+ else if (msgType == "SessionInit") {
+ QVariantMap map = m["SessionState"].toMap();
+ handle(SessionState(map["Identities"].toList(), map["BufferInfos"].toList(), map["NetworkIds"].toList()));
+ }
+
+ else {
+ emit protocolError(tr("Unknown protocol message of type %1").arg(msgType));
+ }
+}
+
+
+void LegacyPeer::dispatch(const RegisterClient &msg) {
+ QVariantMap m;
+ m["MsgType"] = "ClientInit";
+ m["ClientVersion"] = msg.clientVersion;
+ m["ClientDate"] = Quassel::buildInfo().buildDate;
+
+ // FIXME only in compat mode
+ m["ProtocolVersion"] = protocolVersion;
+ m["UseSsl"] = msg.sslSupported;
+#ifndef QT_NO_COMPRESS
+ m["UseCompression"] = true;
+#else
+ m["UseCompression"] = false;
+#endif
+
+ writeSocketData(m);
+}
+
+
+void LegacyPeer::dispatch(const ClientDenied &msg) {
+ QVariantMap m;
+ m["MsgType"] = "ClientInitReject";
+ m["Error"] = msg.errorString;
+
+ writeSocketData(m);
+}
+
+
+void LegacyPeer::dispatch(const ClientRegistered &msg) {
+ QVariantMap m;
+ m["MsgType"] = "ClientInitAck";
+ m["CoreFeatures"] = msg.coreFeatures;
+ m["StorageBackends"] = msg.backendInfo;
+
+ // FIXME only in compat mode
+ m["ProtocolVersion"] = protocolVersion;
+ m["SupportSsl"] = msg.sslSupported;
+ m["SupportsCompression"] = socket()->property("UseCompression"); // this property gets already set in the ClientInit handler
+
+ // This is only used for old v10 clients (pre-0.5)
+ int uptime = msg.coreStartTime.secsTo(QDateTime::currentDateTime().toUTC());
+ int updays = uptime / 86400; uptime %= 86400;
+ int uphours = uptime / 3600; uptime %= 3600;
+ int upmins = uptime / 60;
+ m["CoreInfo"] = tr("<b>Quassel Core Version %1</b><br>"
+ "Built: %2<br>"
+ "Up %3d%4h%5m (since %6)").arg(Quassel::buildInfo().fancyVersionString)
+ .arg(Quassel::buildInfo().buildDate)
+ .arg(updays).arg(uphours, 2, 10, QChar('0')).arg(upmins, 2, 10, QChar('0')).arg(msg.coreStartTime.toString(Qt::TextDate));
+
+ m["LoginEnabled"] = m["Configured"] = msg.coreConfigured;
+
+ writeSocketData(m);
+ socket()->flush(); // ensure that the write cache is flushed before we switch to ssl
+}
+
+
+void LegacyPeer::dispatch(const SetupData &msg)
+{
+ QVariantMap map;
+ map["AdminUser"] = msg.adminUser;
+ map["AdminPasswd"] = msg.adminPassword;
+ map["Backend"] = msg.backend;
+ map["ConnectionProperties"] = msg.setupData;
+
+ QVariantMap m;
+ m["MsgType"] = "CoreSetupData";
+ m["SetupData"] = map;
+ writeSocketData(m);
+}
+
+
+void LegacyPeer::dispatch(const SetupFailed &msg)
+{
+ QVariantMap m;
+ m["MsgType"] = "CoreSetupReject";
+ m["Error"] = msg.errorString;
+
+ writeSocketData(m);
+}
+
+
+void LegacyPeer::dispatch(const SetupDone &msg)
+{
+ Q_UNUSED(msg)
+
+ QVariantMap m;
+ m["MsgType"] = "CoreSetupAck";
+
+ writeSocketData(m);
+}
+
+
+void LegacyPeer::dispatch(const Login &msg)
+{
+ QVariantMap m;
+ m["MsgType"] = "ClientLogin";
+ m["User"] = msg.user;
+ m["Password"] = msg.password;
+
+ writeSocketData(m);
+}
+
+
+void LegacyPeer::dispatch(const LoginFailed &msg)
+{
+ QVariantMap m;
+ m["MsgType"] = "ClientLoginReject";
+ m["Error"] = msg.errorString;
+
+ writeSocketData(m);
+}
+
+
+void LegacyPeer::dispatch(const LoginSuccess &msg)
+{
+ Q_UNUSED(msg)
+
+ QVariantMap m;
+ m["MsgType"] = "ClientLoginAck";
+
+ writeSocketData(m);
+}
+
+
+void LegacyPeer::dispatch(const SessionState &msg)
+{
+ QVariantMap m;
+ m["MsgType"] = "SessionInit";
+
+ QVariantMap map;
+ map["BufferInfos"] = msg.bufferInfos;
+ map["NetworkIds"] = msg.networkIds;
+ map["Identities"] = msg.identities;
+ m["SessionState"] = map;
+
+ writeSocketData(m);
+}
+
+
+/*** Standard messages ***/
+
void LegacyPeer::handlePackedFunc(const QVariant &packedFunc)
{
QVariantList params(packedFunc.toList());
return;
}
+ // TODO: make sure that this is a valid request type
RequestType requestType = (RequestType)params.takeFirst().value<int>();
switch (requestType) {
case Sync: {
HeartBeatReply
};
- LegacyPeer(QTcpSocket *socket, QObject *parent = 0);
+ LegacyPeer(AuthHandler *authHandler, QTcpSocket *socket, QObject *parent = 0);
~LegacyPeer() {}
void setSignalProxy(SignalProxy *proxy);
+ void dispatch(const Protocol::RegisterClient &msg);
+ void dispatch(const Protocol::ClientDenied &msg);
+ void dispatch(const Protocol::ClientRegistered &msg);
+ void dispatch(const Protocol::SetupData &msg);
+ void dispatch(const Protocol::SetupFailed &msg);
+ void dispatch(const Protocol::SetupDone &msg);
+ void dispatch(const Protocol::Login &msg);
+ void dispatch(const Protocol::LoginFailed &msg);
+ void dispatch(const Protocol::LoginSuccess &msg);
+ void dispatch(const Protocol::SessionState &msg);
+
void dispatch(const Protocol::SyncMessage &msg);
void dispatch(const Protocol::RpcCall &msg);
void dispatch(const Protocol::InitRequest &msg);
// FIXME: this is only used for the auth phase and should be replaced by something more generic
void writeSocketData(const QVariant &item);
+signals:
+ void protocolError(const QString &errorString);
+
+ // only used in compat mode
+ void protocolVersionMismatch(int actual, int expected);
+
private slots:
void socketDataAvailable();
private:
bool readSocketData(QVariant &item);
+ void handleHandshakeMessage(const QVariant &msg);
void handlePackedFunc(const QVariant &packedFunc);
void dispatchPackedFunc(const QVariantList &packedFunc);
using namespace Protocol;
-RemotePeer::RemotePeer(QTcpSocket *socket, QObject *parent)
- : Peer(parent),
+RemotePeer::RemotePeer(::AuthHandler *authHandler, QTcpSocket *socket, QObject *parent)
+ : Peer(authHandler, parent),
_socket(socket),
_signalProxy(0),
_heartBeatTimer(new QTimer(this)),
{
socket->setParent(this);
connect(socket, SIGNAL(disconnected()), SIGNAL(disconnected()));
- connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SIGNAL(error(QAbstractSocket::SocketError)));
+ connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SIGNAL(socketStateChanged(QAbstractSocket::SocketState)));
+ connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(onSocketError(QAbstractSocket::SocketError)));
#ifdef HAVE_SSL
QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket);
}
+void RemotePeer::onSocketError(QAbstractSocket::SocketError error)
+{
+ emit socketError(error, socket()->errorString());
+}
+
+
QString RemotePeer::description() const
{
if (socket())
{
_heartBeatCount = 0;
#if QT_VERSION >= 0x040900
- emit lagUpdated(heartBeatReply.timestamp().msecsTo(QDateTime::currentDateTime().toUTC()) / 2);
+ emit lagUpdated(heartBeatReply.timestamp.msecsTo(QDateTime::currentDateTime().toUTC()) / 2);
#else
- emit lagUpdated(heartBeatReply.timestamp().time().msecsTo(QDateTime::currentDateTime().toUTC().time()) / 2);
+ emit lagUpdated(heartBeatReply.timestamp.time().msecsTo(QDateTime::currentDateTime().toUTC().time()) / 2);
#endif
}
class QTcpSocket;
class QTimer;
+class AuthHandler;
+
class RemotePeer : public Peer
{
Q_OBJECT
public:
- RemotePeer(QTcpSocket *socket, QObject *parent = 0);
+ // import the virtuals from the baseclass
+ using Peer::handle;
+ using Peer::dispatch;
+
+ RemotePeer(AuthHandler *authHandler, QTcpSocket *socket, QObject *parent = 0);
virtual ~RemotePeer() {};
void setSignalProxy(SignalProxy *proxy);
QTcpSocket *socket() const;
- // this is only used for the auth phase and should be replaced by something more generic
- virtual void writeSocketData(const QVariant &item) = 0;
-
public slots:
void close(const QString &reason = QString());
signals:
- // this is only used for the auth phase and should be replaced by something more generic
- void dataReceived(const QVariant &item);
-
void transferProgress(int current, int max);
+ void socketStateChanged(QAbstractSocket::SocketState socketState);
+ void socketError(QAbstractSocket::SocketError error, const QString &errorString);
protected:
SignalProxy *signalProxy() const;
- using Peer::handle;
- using Peer::dispatch;
-
// These protocol messages get handled internally and won't reach SignalProxy
void handle(const Protocol::HeartBeat &heartBeat);
void handle(const Protocol::HeartBeatReply &heartBeatReply);
private slots:
void sendHeartBeat();
void changeHeartBeatInterval(int secs);
+ void onSocketError(QAbstractSocket::SocketError error);
private:
QTcpSocket *_socket;
void handle(Peer *peer, const Protocol::InitData &initData);
template<class T>
- void handle(Peer *peer, T) { Q_UNUSED(peer); Q_ASSERT(0); }
+ void handle(Peer *, T) { Q_ASSERT(0); }
bool invokeSlot(QObject *receiver, int methodId, const QVariantList ¶ms, QVariant &returnValue);
bool invokeSlot(QObject *receiver, int methodId, const QVariantList ¶ms = QVariantList());
core.cpp
corealiasmanager.cpp
coreapplication.cpp
+ coreauthhandler.cpp
corebacklogmanager.cpp
corebasichandler.cpp
corebuffersyncer.cpp
core.h
corealiasmanager.h
coreapplication.h
+ coreauthhandler.h
corebacklogmanager.h
corebasichandler.h
corebuffersyncer.h
#include <QCoreApplication>
#include "core.h"
+#include "coreauthhandler.h"
#include "coresession.h"
#include "coresettings.h"
+#include "logger.h"
#include "internalpeer.h"
+#include "network.h"
#include "postgresqlstorage.h"
#include "quassel.h"
#include "sqlitestorage.h"
-#include "network.h"
-#include "logger.h"
-
#include "util.h"
#include "protocols/legacy/legacypeer.h"
Core::Core()
- : _storage(0)
+ : QObject(),
+ _storage(0)
{
#ifdef HAVE_UMASK
umask(S_IRWXG | S_IRWXO);
void Core::init()
{
CoreSettings cs;
- _configured = initStorage(cs.storageSettings().toMap());
+ // legacy
+ QVariantMap dbsettings = cs.storageSettings().toMap();
+ _configured = initStorage(dbsettings.value("Backend").toString(), dbsettings.value("ConnectionProperties").toMap());
if (Quassel::isOptionSet("select-backend")) {
selectBackend(Quassel::optionValue("select-backend"));
Core::~Core()
{
- foreach(RemotePeer *peer, clientInfo.keys()) {
- peer->close(); // disconnect non authed clients
+ // FIXME do we need more cleanup for handlers?
+ foreach(CoreAuthHandler *handler, _connectingClients) {
+ handler->deleteLater(); // disconnect non authed clients
}
qDeleteAll(sessions);
qDeleteAll(_storageBackends);
/*** Core Setup ***/
-QString Core::setupCoreForInternalUsage()
+
+QString Core::setup(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData)
{
- Q_ASSERT(!_storageBackends.isEmpty());
- QVariantMap setupData;
- qsrand(QDateTime::currentDateTime().toTime_t());
- int pass = 0;
- for (int i = 0; i < 10; i++) {
- pass *= 10;
- pass += qrand() % 10;
- }
- setupData["AdminUser"] = "AdminUser";
- setupData["AdminPasswd"] = QString::number(pass);
- setupData["Backend"] = QString("SQLite"); // mono client currently needs sqlite
- return setupCore(setupData);
+ return instance()->setupCore(adminUser, adminPassword, backend, setupData);
}
-QString Core::setupCore(QVariantMap setupData)
+QString Core::setupCore(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData)
{
- QString user = setupData.take("AdminUser").toString();
- QString password = setupData.take("AdminPasswd").toString();
- if (user.isEmpty() || password.isEmpty()) {
+ if (_configured)
+ return tr("Core is already configured! Not configuring again...");
+
+ if (adminUser.isEmpty() || adminPassword.isEmpty()) {
return tr("Admin user or password not set.");
}
- if (_configured || !(_configured = initStorage(setupData, true))) {
+ if (!(_configured = initStorage(backend, setupData, true))) {
return tr("Could not setup storage!");
}
- CoreSettings s;
- s.setStorageSettings(setupData);
+
+ saveBackendSettings(backend, setupData);
+
quInfo() << qPrintable(tr("Creating admin user..."));
- _storage->addUser(user, password);
+ _storage->addUser(adminUser, adminPassword);
startListening(); // TODO check when we need this
return QString();
}
+QString Core::setupCoreForInternalUsage()
+{
+ Q_ASSERT(!_storageBackends.isEmpty());
+
+ qsrand(QDateTime::currentDateTime().toTime_t());
+ int pass = 0;
+ for (int i = 0; i < 10; i++) {
+ pass *= 10;
+ pass += qrand() % 10;
+ }
+
+ // mono client currently needs sqlite
+ return setupCore("AdminUser", QString::number(pass), "SQLite", QVariantMap());
+}
+
+
/*** Storage Handling ***/
void Core::registerStorageBackends()
{
// old db settings:
// "Type" => "sqlite"
-bool Core::initStorage(const QString &backend, QVariantMap settings, bool setup)
+bool Core::initStorage(const QString &backend, const QVariantMap &settings, bool setup)
{
_storage = 0;
}
-bool Core::initStorage(QVariantMap dbSettings, bool setup)
-{
- return initStorage(dbSettings["Backend"].toString(), dbSettings["ConnectionProperties"].toMap(), setup);
-}
-
-
void Core::syncStorage()
{
if (_storage)
/*** Network Management ***/
+bool Core::sslSupported()
+{
+#ifdef HAVE_SSL
+ SslServer *sslServer = qobject_cast<SslServer *>(&instance()->_server);
+ return sslServer && sslServer->isCertValid();
+#else
+ return false;
+#endif
+}
+
+
bool Core::startListening()
{
// in mono mode we only start a local port if a port is specified in the cli call
Q_ASSERT(server);
while (server->hasPendingConnections()) {
QTcpSocket *socket = server->nextPendingConnection();
- RemotePeer *peer = new LegacyPeer(socket, this);
- connect(peer, SIGNAL(disconnected()), SLOT(clientDisconnected()));
- connect(peer, SIGNAL(dataReceived(QVariant)), SLOT(processClientMessage(QVariant)));
- connect(peer, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError(QAbstractSocket::SocketError)));
+ CoreAuthHandler *handler = new CoreAuthHandler(socket, this);
+ _connectingClients.insert(handler);
+
+ connect(handler, SIGNAL(disconnected()), SLOT(clientDisconnected()));
+ connect(handler, SIGNAL(socketError(QAbstractSocket::SocketError,QString)), SLOT(socketError(QAbstractSocket::SocketError,QString)));
+ connect(handler, SIGNAL(handshakeComplete(RemotePeer*,UserId)), SLOT(setupClientSession(RemotePeer*,UserId)));
- clientInfo.insert(peer, QVariantMap());
quInfo() << qPrintable(tr("Client connected from")) << qPrintable(socket->peerAddress().toString());
if (!_configured) {
}
-void Core::processClientMessage(const QVariant &data)
-{
- RemotePeer *peer = qobject_cast<RemotePeer *>(sender());
- if (!peer) {
- qWarning() << Q_FUNC_INFO << "Message not sent by RemoteConnection!";
- return;
- }
-
- QVariantMap msg = data.toMap();
- if (!msg.contains("MsgType")) {
- // Client is way too old, does not even use the current init format
- qWarning() << qPrintable(tr("Antique client trying to connect... refusing."));
- peer->close();
- return;
- }
-
- // OK, so we have at least an init message format we can understand
- if (msg["MsgType"] == "ClientInit") {
- QVariantMap reply;
-
- // Just version information -- check it!
- uint ver = msg["ProtocolVersion"].toUInt();
- if (ver < Quassel::buildInfo().coreNeedsProtocol) {
- reply["MsgType"] = "ClientInitReject";
- reply["Error"] = tr("<b>Your Quassel Client is too old!</b><br>"
- "This core needs at least client/core protocol version %1.<br>"
- "Please consider upgrading your client.").arg(Quassel::buildInfo().coreNeedsProtocol);
- peer->writeSocketData(reply);
- qWarning() << qPrintable(tr("Client")) << peer->description() << qPrintable(tr("too old, rejecting."));
- peer->close();
- return;
- }
-
- reply["ProtocolVersion"] = Quassel::buildInfo().protocolVersion;
- reply["CoreVersion"] = Quassel::buildInfo().fancyVersionString;
- reply["CoreDate"] = Quassel::buildInfo().buildDate;
- reply["CoreStartTime"] = startTime(); // v10 clients don't necessarily parse this, see below
-
- // FIXME: newer clients no longer use the hardcoded CoreInfo (for now), since it gets the
- // time zone wrong. With the next protocol bump (10 -> 11), we should remove this
- // or make it properly configurable.
-
- int uptime = startTime().secsTo(QDateTime::currentDateTime().toUTC());
- int updays = uptime / 86400; uptime %= 86400;
- int uphours = uptime / 3600; uptime %= 3600;
- int upmins = uptime / 60;
- reply["CoreInfo"] = tr("<b>Quassel Core Version %1</b><br>"
- "Built: %2<br>"
- "Up %3d%4h%5m (since %6)").arg(Quassel::buildInfo().fancyVersionString)
- .arg(Quassel::buildInfo().buildDate)
- .arg(updays).arg(uphours, 2, 10, QChar('0')).arg(upmins, 2, 10, QChar('0')).arg(startTime().toString(Qt::TextDate));
-
- reply["CoreFeatures"] = (int)Quassel::features();
-
-#ifdef HAVE_SSL
- SslServer *sslServer = qobject_cast<SslServer *>(&_server);
- QSslSocket *sslSocket = qobject_cast<QSslSocket *>(peer->socket());
- bool supportSsl = sslServer && sslSocket && sslServer->isCertValid();
-#else
- bool supportSsl = false;
-#endif
-
-#ifndef QT_NO_COMPRESS
- bool supportsCompression = true;
-#else
- bool supportsCompression = false;
-#endif
-
- reply["SupportSsl"] = supportSsl;
- reply["SupportsCompression"] = supportsCompression;
- // switch to ssl/compression after client has been informed about our capabilities (see below)
-
- reply["LoginEnabled"] = true;
-
- // check if we are configured, start wizard otherwise
- if (!_configured) {
- reply["Configured"] = false;
- QList<QVariant> backends;
- foreach(Storage *backend, _storageBackends.values()) {
- QVariantMap v;
- v["DisplayName"] = backend->displayName();
- v["Description"] = backend->description();
- v["SetupKeys"] = backend->setupKeys();
- v["SetupDefaults"] = backend->setupDefaults();
- backends.append(v);
- }
- reply["StorageBackends"] = backends;
- reply["LoginEnabled"] = false;
- }
- else {
- reply["Configured"] = true;
- }
- clientInfo[peer] = msg; // store for future reference
- reply["MsgType"] = "ClientInitAck";
- peer->writeSocketData(reply);
- peer->socket()->flush(); // ensure that the write cache is flushed before we switch to ssl
-
-#ifdef HAVE_SSL
- // after we told the client that we are ssl capable we switch to ssl mode
- if (supportSsl && msg["UseSsl"].toBool()) {
- qDebug() << qPrintable(tr("Starting TLS for Client:")) << peer->description();
- connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError> &)), SLOT(sslErrors(const QList<QSslError> &)));
- sslSocket->startServerEncryption();
- }
-#endif
-
-#ifndef QT_NO_COMPRESS
- if (supportsCompression && msg["UseCompression"].toBool()) {
- peer->socket()->setProperty("UseCompression", true);
- qDebug() << "Using compression for Client:" << qPrintable(peer->socket()->peerAddress().toString());
- }
-#endif
- }
- else {
- // for the rest, we need an initialized connection
- if (!clientInfo.contains(peer)) {
- QVariantMap reply;
- reply["MsgType"] = "ClientLoginReject";
- reply["Error"] = tr("<b>Client not initialized!</b><br>You need to send an init message before trying to login.");
- peer->writeSocketData(reply);
- qWarning() << qPrintable(tr("Client")) << qPrintable(peer->socket()->peerAddress().toString()) << qPrintable(tr("did not send an init message before trying to login, rejecting."));
- peer->close(); return;
- }
- if (msg["MsgType"] == "CoreSetupData") {
- QVariantMap reply;
- QString result = setupCore(msg["SetupData"].toMap());
- if (!result.isEmpty()) {
- reply["MsgType"] = "CoreSetupReject";
- reply["Error"] = result;
- }
- else {
- reply["MsgType"] = "CoreSetupAck";
- }
- peer->writeSocketData(reply);
- }
- else if (msg["MsgType"] == "ClientLogin") {
- QVariantMap reply;
- UserId uid = _storage->validateUser(msg["User"].toString(), msg["Password"].toString());
- if (uid == 0) {
- reply["MsgType"] = "ClientLoginReject";
- reply["Error"] = tr("<b>Invalid username or password!</b><br>The username/password combination you supplied could not be found in the database.");
- peer->writeSocketData(reply);
- return;
- }
- reply["MsgType"] = "ClientLoginAck";
- peer->writeSocketData(reply);
- quInfo() << qPrintable(tr("Client")) << qPrintable(peer->socket()->peerAddress().toString()) << qPrintable(tr("initialized and authenticated successfully as \"%1\" (UserId: %2).").arg(msg["User"].toString()).arg(uid.toInt()));
- setupClientSession(peer, uid);
- }
- }
-}
-
-
// Potentially called during the initialization phase (before handing the connection off to the session)
void Core::clientDisconnected()
{
- RemotePeer *peer = qobject_cast<RemotePeer *>(sender());
- Q_ASSERT(peer);
+ CoreAuthHandler *handler = qobject_cast<CoreAuthHandler *>(sender());
+ Q_ASSERT(handler);
- quInfo() << qPrintable(tr("Non-authed client disconnected.")) << qPrintable(peer->socket()->peerAddress().toString());
- clientInfo.remove(peer);
- peer->deleteLater();
+ quInfo() << qPrintable(tr("Non-authed client disconnected:")) << qPrintable(handler->socket()->peerAddress().toString());
+ _connectingClients.remove(handler);
+ handler->deleteLater();
// make server listen again if still not configured
if (!_configured) {
void Core::setupClientSession(RemotePeer *peer, UserId uid)
{
+ CoreAuthHandler *handler = qobject_cast<CoreAuthHandler *>(sender());
+ Q_ASSERT(handler);
+
// From now on everything is handled by the client session
- disconnect(peer, 0, this, 0);
- peer->socket()->flush();
- clientInfo.remove(peer);
+ disconnect(handler, 0, this, 0);
+ _connectingClients.remove(handler);
+ handler->deleteLater();
// Find or create session for validated user
SessionThread *session;
else {
session = createSession(uid);
if (!session) {
- qWarning() << qPrintable(tr("Could not initialize session for client:")) << qPrintable(peer->socket()->peerAddress().toString());
+ qWarning() << qPrintable(tr("Could not initialize session for client:")) << qPrintable(peer->description());
peer->close();
+ peer->deleteLater();
return;
}
}
{
// Find or create session for validated user
if (!sessions.contains(uid)) {
- qWarning() << qPrintable(tr("Could not find a session for client:")) << qPrintable(peer->socket()->peerAddress().toString());
+ qWarning() << qPrintable(tr("Could not find a session for client:")) << qPrintable(peer->description());
peer->close();
+ peer->deleteLater();
return;
}
}
-#ifdef HAVE_SSL
-void Core::sslErrors(const QList<QSslError> &errors)
+void Core::socketError(QAbstractSocket::SocketError err, const QString &errorString)
{
- Q_UNUSED(errors);
- QSslSocket *socket = qobject_cast<QSslSocket *>(sender());
- if (socket)
- socket->ignoreSslErrors();
+ qWarning() << QString("Socket error %1: %2").arg(err).arg(errorString);
}
-#endif
-
-void Core::socketError(QAbstractSocket::SocketError err)
+QVariantList Core::backendInfo()
{
- RemotePeer *peer = qobject_cast<RemotePeer *>(sender());
- if (peer && err != QAbstractSocket::RemoteHostClosedError)
- qWarning() << "Core::socketError()" << peer->socket() << err << peer->socket()->errorString();
+ QVariantList backends;
+ foreach(const Storage *backend, instance()->_storageBackends.values()) {
+ QVariantMap v;
+ v["DisplayName"] = backend->displayName();
+ v["Description"] = backend->description();
+ v["SetupKeys"] = backend->setupKeys();
+ v["SetupDefaults"] = backend->setupDefaults();
+ backends.append(v);
+ }
+ return backends;
}
#include "storage.h"
#include "types.h"
+class CoreAuthHandler;
class CoreSession;
-class RemotePeer;
struct NetworkInfo;
class SessionThread;
class SignalProxy;
/*** Storage access ***/
// These methods are threadsafe.
+ //! Validate user
+ /**
+ * \param userName The user's login name
+ * \param password The user's uncrypted password
+ * \return The user's ID if valid; 0 otherwise
+ */
+ static inline UserId validateUser(const QString &userName, const QString &password) {
+ return instance()->_storage->validateUser(userName, password);
+ }
+
//! Store a user setting persistently
/**
* \param userId The users Id
}
- const QDateTime &startTime() const { return _startTime; }
+ static inline QDateTime startTime() { return instance()->_startTime; }
+ static inline bool isConfigured() { return instance()->_configured; }
+ static bool sslSupported();
+ static QVariantList backendInfo();
+
+ static QString setup(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData);
static inline QTimer &syncTimer() { return instance()->_storageSyncTimer; }
*/
void syncStorage();
void setupInternalClientSession(InternalPeer *clientConnection);
+ QString setupCore(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData);
signals:
//! Sent when a BufferInfo is updated in storage.
void bufferInfoUpdated(UserId user, const BufferInfo &info);
- //! Relay From CoreSession::sessionState(const QVariant &). Used for internal connection only
- void sessionState(const QVariant &);
+ //! Relay from CoreSession::sessionState(). Used for internal connection only
+ void sessionState(const Protocol::SessionState &sessionState);
protected:
virtual void customEvent(QEvent *event);
void incomingConnection();
void clientDisconnected();
- bool initStorage(const QString &backend, QVariantMap settings, bool setup = false);
- bool initStorage(QVariantMap dbSettings, bool setup = false);
+ bool initStorage(const QString &backend, const QVariantMap &settings, bool setup = false);
-#ifdef HAVE_SSL
- void sslErrors(const QList<QSslError> &errors);
-#endif
- void socketError(QAbstractSocket::SocketError);
-
- void processClientMessage(const QVariant &data);
+ void socketError(QAbstractSocket::SocketError err, const QString &errorString);
+ void setupClientSession(RemotePeer *, UserId);
private:
Core();
static Core *instanceptr;
SessionThread *createSession(UserId userId, bool restoreState = false);
- void setupClientSession(RemotePeer *peer, UserId uid);
void addClientHelper(RemotePeer *peer, UserId uid);
//void processCoreSetup(QTcpSocket *socket, QVariantMap &msg);
QString setupCoreForInternalUsage();
- QString setupCore(QVariantMap setupData);
void registerStorageBackends();
bool registerStorageBackend(Storage *);
void saveBackendSettings(const QString &backend, const QVariantMap &settings);
QVariantMap promptForSettings(const Storage *storage);
+private:
+ QSet<CoreAuthHandler *> _connectingClients;
QHash<UserId, SessionThread *> sessions;
Storage *_storage;
QTimer _storageSyncTimer;
OidentdConfigGenerator *_oidentdConfigGenerator;
- QHash<RemotePeer *, QVariantMap> clientInfo;
-
QHash<QString, Storage *> _storageBackends;
QDateTime _startTime;
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-2013 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "coreauthhandler.h"
+
+#ifdef HAVE_SSL
+# include <QSslSocket>
+#endif
+
+#include "core.h"
+#include "logger.h"
+
+#include "protocols/legacy/legacypeer.h"
+
+using namespace Protocol;
+
+CoreAuthHandler::CoreAuthHandler(QTcpSocket *socket, QObject *parent)
+ : AuthHandler(parent)
+ , _peer(0)
+ , _clientRegistered(false)
+{
+ setSocket(socket);
+
+ // TODO: protocol detection
+
+ // FIXME: make sure _peer gets deleted
+ // TODO: socket ownership goes to the peer! (-> use shared ptr later...)
+ _peer = new LegacyPeer(this, socket, this);
+ // only in compat mode
+ connect(_peer, SIGNAL(protocolVersionMismatch(int,int)), SLOT(onProtocolVersionMismatch(int,int)));
+}
+
+
+// only in compat mode
+void CoreAuthHandler::onProtocolVersionMismatch(int actual, int expected)
+{
+ qWarning() << qPrintable(tr("Client")) << _peer->description() << qPrintable(tr("too old, rejecting."));
+ QString errorString = tr("<b>Your Quassel Client is too old!</b><br>"
+ "This core needs at least client/core protocol version %1 (got: %2).<br>"
+ "Please consider upgrading your client.").arg(expected, actual);
+ _peer->dispatch(ClientDenied(errorString));
+ _peer->close();
+}
+
+
+void CoreAuthHandler::startSsl()
+{
+#ifdef HAVE_SSL
+ QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket());
+ Q_ASSERT(sslSocket);
+
+ qDebug() << qPrintable(tr("Starting encryption for Client:")) << _peer->description();
+ connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError> &)), SLOT(onSslErrors()));
+ sslSocket->startServerEncryption();
+#endif
+}
+
+
+#ifdef HAVE_SSL
+void CoreAuthHandler::onSslErrors()
+{
+ QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket());
+ Q_ASSERT(sslSocket);
+ sslSocket->ignoreSslErrors();
+}
+#endif
+
+
+bool CoreAuthHandler::checkClientRegistered()
+{
+ if (!_clientRegistered) {
+ qWarning() << qPrintable(tr("Client")) << qPrintable(socket()->peerAddress().toString()) << qPrintable(tr("did not send an init message before trying to login, rejecting."));
+ _peer->dispatch(ClientDenied(tr("<b>Client not initialized!</b><br>You need to send an init message before trying to login.")));
+ _peer->close();
+ return false;
+ }
+ return true;
+}
+
+
+void CoreAuthHandler::handle(const RegisterClient &msg)
+{
+ // TODO: only in compat mode
+ bool useSsl = false;
+#ifdef HAVE_SSL
+ if (Core::sslSupported() && msg.sslSupported)
+ useSsl = true;
+#endif
+ QVariantList backends;
+ bool configured = Core::isConfigured();
+ if (!configured)
+ backends = Core::backendInfo();
+
+ _peer->dispatch(ClientRegistered(Quassel::features(), configured, backends, useSsl, Core::instance()->startTime()));
+ // TODO: only in compat mode
+ if (useSsl)
+ startSsl();
+
+ _clientRegistered = true;
+}
+
+
+void CoreAuthHandler::handle(const SetupData &msg)
+{
+ if (!checkClientRegistered())
+ return;
+
+ QString result = Core::setup(msg.adminUser, msg.adminPassword, msg.backend, msg.setupData);
+ if (!result.isEmpty())
+ _peer->dispatch(SetupFailed(result));
+ else
+ _peer->dispatch(SetupDone());
+}
+
+
+void CoreAuthHandler::handle(const Login &msg)
+{
+ if (!checkClientRegistered())
+ return;
+
+ UserId uid = Core::validateUser(msg.user, msg.password);
+ if (uid == 0) {
+ _peer->dispatch(LoginFailed(tr("<b>Invalid username or password!</b><br>The username/password combination you supplied could not be found in the database.")));
+ return;
+ }
+ _peer->dispatch(LoginSuccess());
+
+ quInfo() << qPrintable(tr("Client %1 initialized and authenticated successfully as \"%2\" (UserId: %3).").arg(socket()->peerAddress().toString(), msg.user, QString::number(uid.toInt())));
+
+ disconnect(socket(), 0, this, 0);
+ disconnect(_peer, 0, this, 0);
+ _peer->setParent(0); // Core needs to take care of this one now!
+
+ emit handshakeComplete(_peer, uid);
+}
--- /dev/null
+/***************************************************************************
+ * Copyright (C) 2005-2013 by the Quassel Project *
+ * devel@quassel-irc.org *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) version 3. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef COREAUTHHANDLER_H
+#define COREAUTHHANDLER_H
+
+#include "authhandler.h"
+#include "remotepeer.h"
+#include "types.h"
+
+class CoreAuthHandler : public AuthHandler
+{
+ Q_OBJECT
+
+public:
+ CoreAuthHandler(QTcpSocket *socket, QObject *parent = 0);
+
+signals:
+ void handshakeComplete(RemotePeer *peer, UserId uid);
+
+private:
+ using AuthHandler::handle;
+
+ void handle(const Protocol::RegisterClient &msg);
+ void handle(const Protocol::SetupData &msg);
+ void handle(const Protocol::Login &msg);
+
+ bool checkClientRegistered();
+
+private slots:
+ void startSsl();
+#ifdef HAVE_SSL
+ void onSslErrors();
+#endif
+
+ // only in compat mode
+ void onProtocolVersionMismatch(int actual, int expected);
+
+private:
+ RemotePeer *_peer;
+
+ bool _clientRegistered;
+};
+
+#endif
void CoreSession::addClient(RemotePeer *peer)
{
- QVariantMap reply;
- reply["MsgType"] = "SessionInit";
- reply["SessionState"] = sessionState();
- peer->writeSocketData(reply);
+ peer->dispatch(sessionState());
signalProxy()->addPeer(peer);
}
}
-QVariant CoreSession::sessionState()
+Protocol::SessionState CoreSession::sessionState() const
{
- QVariantMap v;
+ QVariantList bufferInfos;
+ QVariantList networkIds;
+ QVariantList identities;
- v["CoreFeatures"] = (int)Quassel::features();
+ foreach(const BufferInfo &id, buffers())
+ bufferInfos << QVariant::fromValue(id);
+ foreach(const NetworkId &id, _networks.keys())
+ networkIds << QVariant::fromValue(id);
+ foreach(const Identity *i, _identities.values())
+ identities << QVariant::fromValue(*i);
- QVariantList bufs;
- foreach(BufferInfo id, buffers()) bufs << qVariantFromValue(id);
- v["BufferInfos"] = bufs;
- QVariantList networkids;
- foreach(NetworkId id, _networks.keys()) networkids << qVariantFromValue(id);
- v["NetworkIds"] = networkids;
-
- quint32 ircusercount = 0;
- quint32 ircchannelcount = 0;
- foreach(Network *net, _networks.values()) {
- ircusercount += net->ircUserCount();
- ircchannelcount += net->ircChannelCount();
- }
- v["IrcUserCount"] = ircusercount;
- v["IrcChannelCount"] = ircchannelcount;
-
- QList<QVariant> idlist;
- foreach(Identity *i, _identities.values()) idlist << qVariantFromValue(*i);
- v["Identities"] = idlist;
-
- //v["Payload"] = QByteArray(100000000, 'a'); // for testing purposes
- return v;
+ return Protocol::SessionState(identities, bufferInfos, networkIds);
}
#include "corecoreinfo.h"
#include "corealiasmanager.h"
#include "coreignorelistmanager.h"
+#include "protocol.h"
#include "message.h"
#include "storage.h"
inline CoreNetworkConfig *networkConfig() const { return _networkConfig; }
NetworkConnection *networkConnection(NetworkId) const;
- QVariant sessionState();
+ Protocol::SessionState sessionState() const;
inline SignalProxy *signalProxy() const { return _signalProxy; }
signals:
void initialized();
- void sessionState(const QVariant &);
+ void sessionState(const Protocol::SessionState &sessionState);
//void msgFromGui(uint netid, QString buf, QString message);
void displayMsg(Message message);
_session = new CoreSession(user(), _restoreState);
connect(this, SIGNAL(addRemoteClient(RemotePeer*)), _session, SLOT(addClient(RemotePeer*)));
connect(this, SIGNAL(addInternalClient(InternalPeer*)), _session, SLOT(addClient(InternalPeer*)));
- connect(_session, SIGNAL(sessionState(QVariant)), Core::instance(), SIGNAL(sessionState(QVariant)));
+ connect(_session, SIGNAL(sessionState(Protocol::SessionState)), Core::instance(), SIGNAL(sessionState(Protocol::SessionState)));
emit initialized();
exec();
delete _session;
}
if (!_cert.isValid()) {
quWarning() << "SslServer: Invalid certificate (most likely expired)";
+ // We allow the core to offer SSL anyway, so no "return false" here. Client will warn about the cert being invalid.
}
if (_key.isNull()) {
quWarning() << "SslServer:" << qPrintable(path) << "contains no key data";
foreach(int idx, visitedPages())
page(idx)->setEnabled(false);
- QVariantMap foo;
- foo["AdminUser"] = field("adminUser.user").toString();
- foo["AdminPasswd"] = field("adminUser.password").toString();
- foo["Backend"] = backend;
- foo["ConnectionProperties"] = properties;
- coreConnection()->doCoreSetup(foo);
+ coreConnection()->setupCore(Protocol::SetupData(field("adminUser.user").toString(), field("adminUser.password").toString(), backend, properties));
}
Core *core = Core::instance();
CoreConnection *connection = Client::coreConnection();
connect(connection, SIGNAL(connectToInternalCore(InternalPeer*)), core, SLOT(setupInternalClientSession(InternalPeer*)));
- connect(core, SIGNAL(sessionState(QVariant)), connection, SLOT(internalSessionStateReceived(QVariant)));
+ connect(core, SIGNAL(sessionState(Protocol::SessionState)), connection, SLOT(internalSessionStateReceived(Protocol::SessionState)));
}