1 /***************************************************************************
2 * Copyright (C) 2009 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
21 #include "coreconnection.h"
23 #ifndef QT_NO_NETWORKPROXY
24 # include <QNetworkProxy>
28 #include "clientsettings.h"
29 #include "coreaccountmodel.h"
32 #include "networkmodel.h"
34 #include "signalproxy.h"
37 CoreConnection::CoreConnection(CoreAccountModel *model, QObject *parent)
45 qRegisterMetaType<ConnectionState>("CoreConnection::ConnectionState");
49 void CoreConnection::init() {
50 connect(Client::signalProxy(), SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
53 void CoreConnection::setProgressText(const QString &text) {
54 if(_progressText != text) {
56 emit progressTextChanged(text);
60 void CoreConnection::setProgressValue(int value) {
61 if(_progressValue != value) {
62 _progressValue = value;
63 emit progressValueChanged(value);
67 void CoreConnection::setProgressMinimum(int minimum) {
68 if(_progressMinimum != minimum) {
69 _progressMinimum = minimum;
70 emit progressRangeChanged(minimum, _progressMaximum);
74 void CoreConnection::setProgressMaximum(int maximum) {
75 if(_progressMaximum != maximum) {
76 _progressMaximum = maximum;
77 emit progressRangeChanged(_progressMinimum, maximum);
81 void CoreConnection::updateProgress(int value, int max) {
82 if(max != _progressMaximum) {
83 _progressMaximum = max;
84 emit progressRangeChanged(_progressMinimum, _progressMaximum);
86 setProgressValue(value);
89 void CoreConnection::resetConnection() {
91 disconnect(_socket, 0, this, 0);
92 _socket->deleteLater();
97 _coreMsgBuffer.clear();
102 setProgressMaximum(-1); // disable
103 emit connectionMsg(tr("Disconnected from core."));
106 void CoreConnection::socketStateChanged(QAbstractSocket::SocketState socketState) {
109 switch(socketState) {
110 case QAbstractSocket::UnconnectedState:
111 text = tr("Disconnected.");
113 case QAbstractSocket::HostLookupState:
114 text = tr("Looking up %1...").arg(currentAccount().hostName());
116 case QAbstractSocket::ConnectingState:
117 text = tr("Connecting to %1...").arg(currentAccount().hostName());
119 case QAbstractSocket::ConnectedState:
120 text = tr("Connected to %1.").arg(currentAccount().hostName());
122 case QAbstractSocket::ClosingState:
123 text = tr("Disconnecting from %1...").arg(currentAccount().hostName());
130 emit progressTextChanged(text);
132 setState(socketState);
135 void CoreConnection::setState(QAbstractSocket::SocketState socketState) {
136 ConnectionState state;
138 switch(socketState) {
139 case QAbstractSocket::UnconnectedState:
140 state = Disconnected;
142 case QAbstractSocket::HostLookupState:
143 case QAbstractSocket::ConnectingState:
146 case QAbstractSocket::ConnectedState:
150 state = Disconnected;
156 void CoreConnection::setState(ConnectionState state) {
157 if(state != _state) {
159 emit stateChanged(state);
163 void CoreConnection::setWarningsHandler(const char *slot) {
164 resetWarningsHandler();
165 connect(this, SIGNAL(handleIgnoreWarnings(bool)), this, slot);
168 void CoreConnection::resetWarningsHandler() {
169 disconnect(this, SIGNAL(handleIgnoreWarnings(bool)), this, 0);
172 void CoreConnection::coreSocketError(QAbstractSocket::SocketError) {
173 qDebug() << "coreSocketError" << _socket << _socket->errorString();
174 emit connectionError(_socket->errorString());
178 void CoreConnection::coreSocketDisconnected() {
179 setState(Disconnected);
182 // FIXME handle disconnects gracefully
185 void CoreConnection::coreHasData() {
187 while(SignalProxy::readDataFromDevice(_socket, _blockSize, item)) {
188 QVariantMap msg = item.toMap();
189 if(!msg.contains("MsgType")) {
190 // This core is way too old and does not even speak our init protocol...
191 emit connectionError(tr("The Quassel Core you try to connect to is too old! Please consider upgrading."));
192 disconnectFromCore();
195 if(msg["MsgType"] == "ClientInitAck") {
197 } else if(msg["MsgType"] == "ClientInitReject") {
198 emit connectionError(msg["Error"].toString());
199 disconnectFromCore();
201 } else if(msg["MsgType"] == "CoreSetupAck") {
202 //emit coreSetupSuccess();
203 } else if(msg["MsgType"] == "CoreSetupReject") {
204 //emit coreSetupFailed(msg["Error"].toString());
205 } else if(msg["MsgType"] == "ClientLoginReject") {
206 loginFailed(msg["Error"].toString());
207 } else if(msg["MsgType"] == "ClientLoginAck") {
209 } else if(msg["MsgType"] == "SessionInit") {
210 // that's it, let's hand over to the signal proxy
211 // if the socket is an orphan, the signalProxy adopts it.
212 // -> we don't need to care about it anymore
213 _socket->setParent(0);
214 Client::signalProxy()->addPeer(_socket);
216 sessionStateReceived(msg["SessionState"].toMap());
217 break; // this is definitively the last message we process here!
219 emit connectionError(tr("<b>Invalid data received from core!</b><br>Disconnecting."));
220 disconnectFromCore();
225 updateProgress(_socket->bytesAvailable(), _blockSize);
229 void CoreConnection::disconnectFromCore() {
231 Client::signalProxy()->removeAllPeers();
236 void CoreConnection::reconnectToCore() {
237 if(currentAccount().isValid())
238 connectToCore(currentAccount().accountId());
241 bool CoreConnection::connectToCore(AccountId accId) {
242 CoreAccountSettings s;
244 if(!accId.isValid()) {
245 // check our settings and figure out what to do
246 if(!s.autoConnectOnStartup())
248 if(s.autoConnectToFixedAccount())
249 accId = s.autoConnectAccount();
251 accId = s.lastAccount();
255 _account = accountModel()->account(accId);
256 if(!_account.accountId().isValid()) {
260 s.setLastAccount(accId);
261 connectToCurrentAccount();
265 void CoreConnection::connectToCurrentAccount() {
270 QSslSocket *sock = new QSslSocket(Client::instance());
272 if(_account.useSsl()) {
273 emit connectionError(tr("<b>This client is built without SSL Support!</b><br />Disable the usage of SSL in the account settings."));
276 QTcpSocket *sock = new QTcpSocket(Client::instance());
279 #ifndef QT_NO_NETWORKPROXY
280 if(_account.useProxy()) {
281 QNetworkProxy proxy(_account.proxyType(), _account.proxyHostName(), _account.proxyPort(), _account.proxyUser(), _account.proxyPassword());
282 sock->setProxy(proxy);
287 connect(sock, SIGNAL(readyRead()), SLOT(coreHasData()));
288 connect(sock, SIGNAL(connected()), SLOT(coreSocketConnected()));
289 connect(sock, SIGNAL(disconnected()), SLOT(coreSocketDisconnected()));
290 connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(coreSocketError(QAbstractSocket::SocketError)));
291 connect(sock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SLOT(socketStateChanged(QAbstractSocket::SocketState)));
293 emit connectionMsg(tr("Connecting to %1...").arg(currentAccount().accountName()));
294 sock->connectToHost(_account.hostName(), _account.port());
297 void CoreConnection::coreSocketConnected() {
298 // Phase One: Send client info and wait for core info
300 emit connectionMsg(tr("Synchronizing to core..."));
302 QVariantMap clientInit;
303 clientInit["MsgType"] = "ClientInit";
304 clientInit["ClientVersion"] = Quassel::buildInfo().fancyVersionString;
305 clientInit["ClientDate"] = Quassel::buildInfo().buildDate;
306 clientInit["ProtocolVersion"] = Quassel::buildInfo().protocolVersion;
307 clientInit["UseSsl"] = _account.useSsl();
308 #ifndef QT_NO_COMPRESS
309 clientInit["UseCompression"] = true;
311 clientInit["UseCompression"] = false;
314 SignalProxy::writeDataToDevice(_socket, clientInit);
317 void CoreConnection::clientInitAck(const QVariantMap &msg) {
318 // Core has accepted our version info and sent its own. Let's see if we accept it as well...
319 uint ver = msg["ProtocolVersion"].toUInt();
320 if(ver < Quassel::buildInfo().clientNeedsProtocol) {
321 emit connectionError(tr("<b>The Quassel Core you are trying to connect to is too old!</b><br>"
322 "Need at least core/client protocol v%1 to connect.").arg(Quassel::buildInfo().clientNeedsProtocol));
323 disconnectFromCore();
327 #ifndef QT_NO_COMPRESS
328 if(msg["SupportsCompression"].toBool()) {
329 _socket->setProperty("UseCompression", true);
333 _coreMsgBuffer = msg;
335 if(currentAccount().useSsl()) {
336 if(msg["SupportSsl"].toBool()) {
337 QSslSocket *sslSocket = qobject_cast<QSslSocket *>(_socket);
339 connect(sslSocket, SIGNAL(encrypted()), this, SLOT(sslSocketEncrypted()));
340 connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(sslErrors(const QList<QSslError> &)));
342 sslSocket->startClientEncryption();
344 emit connectionError(tr("<b>The Quassel Core you are trying to connect to does not support SSL!</b><br />If you want to connect anyways, disable the usage of SSL in the account settings."));
345 disconnectFromCore();
350 // if we use SSL we wait for the next step until every SSL warning has been cleared
355 void CoreConnection::connectionReady() {
356 if(!_coreMsgBuffer["Configured"].toBool()) {
358 emit startCoreSetup(_coreMsgBuffer["StorageBackends"].toList());
359 } else if(_coreMsgBuffer["LoginEnabled"].toBool()) {
362 _coreMsgBuffer.clear();
363 resetWarningsHandler();
366 void CoreConnection::loginToCore() {
367 emit connectionMsg(tr("Logging in..."));
368 if(currentAccount().user().isEmpty() || currentAccount().password().isEmpty()) {
369 emit userAuthenticationRequired(&_account); // *must* be a synchronous call
370 if(currentAccount().user().isEmpty() || currentAccount().password().isEmpty()) {
371 disconnectFromCore();
376 QVariantMap clientLogin;
377 clientLogin["MsgType"] = "ClientLogin";
378 clientLogin["User"] = currentAccount().user();
379 clientLogin["Password"] = currentAccount().password();
380 SignalProxy::writeDataToDevice(_socket, clientLogin);
383 void CoreConnection::loginFailed(const QString &error) {
384 emit userAuthenticationRequired(&_account, error); // *must* be a synchronous call
385 if(currentAccount().user().isEmpty() || currentAccount().password().isEmpty()) {
386 disconnectFromCore();
392 void CoreConnection::loginSuccess() {
393 updateProgress(0, 0);
394 setProgressText(tr("Receiving session state"));
395 setState(Synchronizing);
396 emit connectionMsg(tr("Synchronizing to %1...").arg(currentAccount().accountName()));
399 void CoreConnection::sessionStateReceived(const QVariantMap &state) {
400 updateProgress(100, 100);
402 // rest of communication happens through SignalProxy...
403 disconnect(_socket, SIGNAL(readyRead()), this, 0);
404 disconnect(_socket, SIGNAL(connected()), this, 0);
406 //Client::instance()->setConnectedToCore(currentAccount().accountId(), _socket);
410 void CoreConnection::syncToCore(const QVariantMap &sessionState) {
411 setProgressText(tr("Receiving network states"));
412 updateProgress(0, 100);
415 foreach(QVariant vid, sessionState["Identities"].toList()) {
416 Client::instance()->coreIdentityCreated(vid.value<Identity>());
420 // FIXME: get rid of this crap -- why?
421 QVariantList bufferinfos = sessionState["BufferInfos"].toList();
422 NetworkModel *networkModel = Client::networkModel();
423 Q_ASSERT(networkModel);
424 foreach(QVariant vinfo, bufferinfos)
425 networkModel->bufferUpdated(vinfo.value<BufferInfo>()); // create BufferItems
427 QVariantList networkids = sessionState["NetworkIds"].toList();
429 // prepare sync progress thingys...
430 // FIXME: Care about removal of networks
431 _numNetsToSync = networkids.count();
432 updateProgress(0, _numNetsToSync);
434 // create network objects
435 foreach(QVariant networkid, networkids) {
436 NetworkId netid = networkid.value<NetworkId>();
437 if(Client::network(netid))
439 Network *net = new Network(netid, Client::instance());
440 _netsToSync.insert(net);
441 connect(net, SIGNAL(initDone()), SLOT(networkInitDone()));
442 connect(net, SIGNAL(destroyed()), SLOT(networkInitDone()));
443 Client::addNetwork(net);
448 void CoreConnection::networkInitDone() {
449 Network *net = qobject_cast<Network *>(sender());
451 disconnect(net, 0, this, 0);
452 _netsToSync.remove(net);
453 updateProgress(_numNetsToSync - _netsToSync.count(), _numNetsToSync);
457 void CoreConnection::checkSyncState() {
458 if(_netsToSync.isEmpty()) {
459 setState(Synchronized);
460 setProgressText(QString());
461 setProgressMaximum(-1);