Fixed those nasty "Client::updateLastSeen(): Unknown buffer $bufferId" messages.
[quassel.git] / src / client / clientsyncer.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-08 by the Quassel IRC Team                         *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
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.                                           *
9  *                                                                         *
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.                          *
14  *                                                                         *
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  ***************************************************************************/
20
21 #include "clientsyncer.h"
22
23 #include "client.h"
24 #include "global.h"
25 #include "identity.h"
26 #include "ircuser.h"
27 #include "ircchannel.h"
28 #include "network.h"
29 #include "signalproxy.h"
30
31
32 ClientSyncer::ClientSyncer(QObject *parent) : QObject(parent) {
33   socket = 0;
34   blockSize = 0;
35
36   connect(Client::signalProxy(), SIGNAL(disconnected()), this, SLOT(coreSocketDisconnected()));
37
38 }
39
40 ClientSyncer::~ClientSyncer() {
41
42
43 }
44
45 void ClientSyncer::coreHasData() {
46   QVariant item;
47   while(SignalProxy::readDataFromDevice(socket, blockSize, item)) {
48     emit recvPartialItem(1,1);
49     QVariantMap msg = item.toMap();
50     if(!msg.contains("MsgType")) {
51       // This core is way too old and does not even speak our init protocol...
52       emit connectionError(tr("The Quassel Core you try to connect to is too old! Please consider upgrading."));
53       disconnectFromCore();
54       return;
55     }
56     if(msg["MsgType"] == "ClientInitAck") {
57       clientInitAck(msg);
58     } else if(msg["MsgType"] == "ClientInitReject") {
59       emit connectionError(msg["Error"].toString());
60       disconnectFromCore();
61       return;
62     } else if(msg["MsgType"] == "CoreSetupAck") {
63       emit coreSetupSuccess();
64     } else if(msg["MsgType"] == "CoreSetupReject") {
65       emit coreSetupFailed(msg["Error"].toString());
66     } else if(msg["MsgType"] == "ClientLoginReject") {
67       emit loginFailed(msg["Error"].toString());
68     } else if(msg["MsgType"] == "ClientLoginAck") {
69       // prevent multiple signal connections
70       disconnect(this, SIGNAL(recvPartialItem(quint32, quint32)), this, SIGNAL(sessionProgress(quint32, quint32)));
71       connect(this, SIGNAL(recvPartialItem(quint32, quint32)), this, SIGNAL(sessionProgress(quint32, quint32)));
72       emit loginSuccess();
73     } else if(msg["MsgType"] == "SessionInit") {
74       sessionStateReceived(msg["SessionState"].toMap());
75       break; // this is definitively the last message we process here!
76     } else {
77       emit connectionError(tr("<b>Invalid data received from core!</b><br>Disconnecting."));
78       disconnectFromCore();
79       return;
80     }
81   }
82   if(blockSize > 0) {
83     emit recvPartialItem(socket->bytesAvailable(), blockSize);
84   }
85 }
86
87 void ClientSyncer::coreSocketError(QAbstractSocket::SocketError) {
88   emit connectionError(socket->errorString());
89   socket->deleteLater();
90 }
91
92 void ClientSyncer::disconnectFromCore() {
93   if(socket) socket->close();
94 }
95
96 void ClientSyncer::connectToCore(const QVariantMap &conn) {
97   // TODO implement SSL
98   coreConnectionInfo = conn;
99   //if(isConnected()) {
100   //  emit coreConnectionError(tr("Already connected to Core!"));
101   //  return;
102   // }
103   if(socket != 0) {
104     socket->deleteLater();
105     socket = 0;
106   }
107   if(conn["Host"].toString().isEmpty()) {
108     emit connectionError(tr("Internal connections not yet supported."));
109     return; // FIXME implement internal connections
110     //clientMode = LocalCore;
111     socket = new QBuffer(this);
112     connect(socket, SIGNAL(readyRead()), this, SLOT(coreHasData()));
113     socket->open(QIODevice::ReadWrite);
114     //QVariant state = connectToLocalCore(coreConnectionInfo["User"].toString(), coreConnectionInfo["Password"].toString());
115     //syncToCore(state);
116     coreSocketConnected();
117   } else {
118     //clientMode = RemoteCore;
119     //emit coreConnectionMsg(tr("Connecting..."));
120     Q_ASSERT(!socket);
121     QTcpSocket *sock = new QTcpSocket(Client::instance());
122     socket = sock;
123     connect(sock, SIGNAL(readyRead()), this, SLOT(coreHasData()));
124     connect(sock, SIGNAL(connected()), this, SLOT(coreSocketConnected()));
125     connect(sock, SIGNAL(disconnected()), this, SLOT(coreSocketDisconnected()));
126     connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(coreSocketError(QAbstractSocket::SocketError)));
127     connect(sock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SIGNAL(socketStateChanged(QAbstractSocket::SocketState)));
128     sock->connectToHost(conn["Host"].toString(), conn["Port"].toUInt());
129   }
130 }
131
132 void ClientSyncer::coreSocketConnected() {
133   //connect(this, SIGNAL(recvPartialItem(uint, uint)), this, SIGNAL(coreConnectionProgress(uint, uint)));
134   // Phase One: Send client info and wait for core info
135
136   //emit coreConnectionMsg(tr("Synchronizing to core..."));
137   QVariantMap clientInit;
138   clientInit["MsgType"] = "ClientInit";
139   clientInit["ClientVersion"] = Global::quasselVersion;
140   clientInit["ClientDate"] = Global::quasselDate;
141   clientInit["ClientBuild"] = Global::quasselBuild; // this is a minimum, since we probably won't update for every commit
142   clientInit["UseSsl"] = false;  // FIXME implement SSL
143   SignalProxy::writeDataToDevice(socket, clientInit);
144 }
145
146 void ClientSyncer::coreSocketDisconnected() {
147   emit socketDisconnected();
148   Client::instance()->disconnectFromCore();
149
150   // FIXME handle disconnects gracefully in here as well!
151
152   coreConnectionInfo.clear();
153   netsToSync.clear();
154   channelsToSync.clear();
155   usersToSync.clear();
156   blockSize = 0;
157   //restartPhaseNull();
158 }
159
160 void ClientSyncer::clientInitAck(const QVariantMap &msg) {
161   // Core has accepted our version info and sent its own. Let's see if we accept it as well...
162   if(msg["CoreBuild"].toUInt() < Global::coreBuildNeeded) {
163     emit connectionError(tr("<b>The Quassel Core you are trying to connect to is too old!</b><br>"
164         "Need at least a Core Version %1 (Build >= %2) to connect.").arg(Global::quasselVersion).arg(Global::coreBuildNeeded));
165     disconnectFromCore();
166     return;
167   }
168   emit connectionMsg(msg["CoreInfo"].toString());
169   if(!msg["Configured"].toBool()) {
170     // start wizard
171     emit startCoreSetup(msg["StorageBackends"].toList());
172   } else if(msg["LoginEnabled"].toBool()) {
173     emit startLogin();
174   }
175 }
176
177 void ClientSyncer::doCoreSetup(const QVariant &setupData) {
178   QVariantMap setup;
179   setup["MsgType"] = "CoreSetupData";
180   setup["SetupData"] = setupData;
181   SignalProxy::writeDataToDevice(socket, setup);
182 }
183
184 void ClientSyncer::loginToCore(const QString &user, const QString &passwd) {
185   emit connectionMsg(tr("Logging in..."));
186   QVariantMap clientLogin;
187   clientLogin["MsgType"] = "ClientLogin";
188   clientLogin["User"] = user;
189   clientLogin["Password"] = passwd;
190   SignalProxy::writeDataToDevice(socket, clientLogin);
191 }
192
193 void ClientSyncer::sessionStateReceived(const QVariantMap &state) {
194   emit sessionProgress(1, 1);
195   disconnect(this, SIGNAL(recvPartialItem(quint32, quint32)), this, SIGNAL(sessionProgress(quint32, quint32)));
196   disconnect(socket, 0, this, 0);  // rest of communication happens through SignalProxy
197   //Client::signalProxy()->addPeer(socket);
198   Client::instance()->setConnectedToCore(socket, coreConnectionInfo["AccountId"].value<AccountId>());
199   syncToCore(state);
200 }
201
202 void ClientSyncer::syncToCore(const QVariantMap &sessionState) {
203
204   // create identities
205   foreach(QVariant vid, sessionState["Identities"].toList()) {
206     Client::instance()->coreIdentityCreated(vid.value<Identity>());
207   }
208
209   // create buffers
210   // FIXME: get rid of this crap
211   QVariantList bufferinfos = sessionState["BufferInfos"].toList();
212   foreach(QVariant vinfo, bufferinfos) Client::buffer(vinfo.value<BufferInfo>());  // create Buffers and BufferItems
213
214   QVariantList networkids = sessionState["NetworkIds"].toList();
215
216   // prepare sync progress thingys... FIXME: Care about removal of networks
217   numNetsToSync = networkids.count();
218   numChannelsToSync = 0; //sessionState["IrcChannelCount"].toUInt();
219   numUsersToSync = 0; // sessionState["IrcUserCount"].toUInt(); qDebug() << numUsersToSync;
220   emit networksProgress(0, numNetsToSync);
221   emit channelsProgress(0, numChannelsToSync);
222   emit ircUsersProgress(0, numUsersToSync);
223
224   // create network objects
225   foreach(QVariant networkid, networkids) {
226     NetworkId netid = networkid.value<NetworkId>();
227     Network *net = new Network(netid, Client::instance());
228     netsToSync.insert(net);
229     connect(net, SIGNAL(initDone()), this, SLOT(networkInitDone()));
230     connect(net, SIGNAL(ircUserInitDone(IrcUser *)), this, SLOT(ircUserInitDone(IrcUser *)));
231     connect(net, SIGNAL(ircUserAdded(IrcUser *)), this, SLOT(ircUserAdded(IrcUser *)));
232     connect(net, SIGNAL(ircUserRemoved(QObject *)), this, SLOT(ircUserRemoved(QObject *)));
233     connect(net, SIGNAL(ircChannelInitDone(IrcChannel *)), this, SLOT(ircChannelInitDone(IrcChannel *)));
234     connect(net, SIGNAL(ircChannelAdded(IrcChannel *)), this, SLOT(ircChannelAdded(IrcChannel *)));
235     connect(net, SIGNAL(ircChannelRemoved(QObject *)), this, SLOT(ircChannelRemoved(QObject *)));
236     Client::addNetwork(net);
237   }
238   checkSyncState();
239 }
240
241 void ClientSyncer::networkInitDone() {
242   netsToSync.remove(sender());
243   emit networksProgress(numNetsToSync - netsToSync.count(), numNetsToSync);
244   checkSyncState();
245 }
246
247 void ClientSyncer::ircChannelInitDone(IrcChannel *chan) {
248   channelsToSync.remove(chan);
249   emit channelsProgress(numChannelsToSync - channelsToSync.count(), numChannelsToSync);
250   checkSyncState();
251 }
252
253 void ClientSyncer::ircChannelAdded(IrcChannel *chan) {
254   if(!chan->isInitialized()) {
255     channelsToSync.insert(chan);
256     numChannelsToSync++;
257     emit channelsProgress(numChannelsToSync - channelsToSync.count(), numChannelsToSync);
258     checkSyncState();
259   }
260 }
261
262 void ClientSyncer::ircChannelRemoved(QObject *chan) {
263   if(channelsToSync.contains(chan)) {
264     numChannelsToSync--;
265     channelsToSync.remove(chan);
266     emit channelsProgress(numChannelsToSync - channelsToSync.count(), numChannelsToSync);
267     checkSyncState();
268   }
269 }
270
271 void ClientSyncer::ircUserInitDone(IrcUser *user) {
272   usersToSync.remove(user);
273   emit ircUsersProgress(numUsersToSync - usersToSync.count(), numUsersToSync);
274   checkSyncState();
275 }
276
277 void ClientSyncer::ircUserAdded(IrcUser *user) {
278   if(!user->isInitialized()) {
279     usersToSync.insert(user);
280     numUsersToSync++;
281     emit ircUsersProgress(numUsersToSync - usersToSync.count(), numUsersToSync);
282     checkSyncState();
283   }
284 }
285
286 void ClientSyncer::ircUserRemoved(QObject *user) {
287   if(usersToSync.contains(user)) {
288     numUsersToSync--;
289     usersToSync.remove(user);
290     emit ircUsersProgress(numUsersToSync - usersToSync.count(), numUsersToSync);
291     checkSyncState();
292   }
293 }
294
295 void ClientSyncer::checkSyncState() {
296   if(usersToSync.count() + channelsToSync.count() + netsToSync.count() == 0) {
297     // done syncing!
298     /*
299     qDebug() << "done";
300     foreach(Network *net, _networks.values()) {
301       //disconnect(net, 0, this, SLOT(networkInitDone()));
302       //disconnect(net, 0, this, SLOT(ircUserInitDone(IrcUser *)));
303       //disconnect(net, 0, this, SLOT(ircUserAdded(IrcUser *)));
304       //disconnect(net, 0, this, SLOT(ircUserRemoved(QObject *)));
305       //disconnect(net, 0, this, SLOT(ircChannelInitDone(IrcChannel *)));
306       //disconnect(net, 0, this, SLOT(ircChannelAdded(IrcChannel *)));
307       //disconnect(net, 0, this, SLOT(ircChannelRemoved(QObject *)));
308       qDebug() << "disconnecting";
309       disconnect(net, SIGNAL(initDone()), this, SLOT(networkInitDone()));
310       disconnect(net, SIGNAL(ircUserInitDone(IrcUser *)), this, SLOT(ircUserInitDone(IrcUser *)));
311       disconnect(net, SIGNAL(ircUserAdded(IrcUser *)), this, SLOT(ircUserAdded(IrcUser *)));
312       disconnect(net, SIGNAL(ircUserRemoved(QObject *)), this, SLOT(ircUserRemoved(QObject *)));
313       disconnect(net, SIGNAL(ircChannelInitDone(IrcChannel *)), this, SLOT(ircChannelInitDone(IrcChannel *)));
314       disconnect(net, SIGNAL(ircChannelAdded(IrcChannel *)), this, SLOT(ircChannelAdded(IrcChannel *)));
315       disconnect(net, SIGNAL(ircChannelRemoved(QObject *)), this, SLOT(ircChannelRemoved(QObject *)));
316     }
317     */
318
319     Client::instance()->setSyncedToCore();
320     emit syncFinished();
321     //emit connected();
322     //emit connectionStateChanged(true);
323
324   }
325 }
326