Only a few small things: made the nicklist at least sorted again and fixed the Nick...
[quassel.git] / src / client / client.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-08 by the Quassel Project                          *
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 "client.h"
22
23 #include "bufferinfo.h"
24 #include "global.h"
25 #include "identity.h"
26 #include "ircchannel.h"
27 #include "ircuser.h"
28 #include "message.h"
29 #include "network.h"
30 #include "networkmodel.h"
31 #include "buffermodel.h"
32 #include "nickmodel.h"
33 #include "quasselui.h"
34 #include "signalproxy.h"
35 #include "util.h"
36
37 QPointer<Client> Client::instanceptr = 0;
38
39 /*** Initialization/destruction ***/
40
41 Client *Client::instance() {
42   if(!instanceptr)
43     instanceptr = new Client();
44   return instanceptr;
45 }
46
47 void Client::destroy() {
48   delete instanceptr;
49 }
50
51 void Client::init(AbstractUi *ui) {
52   instance()->mainUi = ui;
53   instance()->init();
54 }
55
56 Client::Client(QObject *parent)
57   : QObject(parent),
58     socket(0),
59     _signalProxy(new SignalProxy(SignalProxy::Client, this)),
60     mainUi(0),
61     _networkModel(0),
62     _bufferModel(0),
63     _nickModel(0),
64     connectedToCore(false)
65 {
66 }
67
68 Client::~Client() {
69 }
70
71 void Client::init() {
72   blockSize = 0;
73
74   _networkModel = new NetworkModel(this);
75   connect(this, SIGNAL(bufferUpdated(BufferInfo)),
76           _networkModel, SLOT(bufferUpdated(BufferInfo)));
77
78   _bufferModel = new BufferModel(_networkModel);
79   _nickModel = new NickModel(_networkModel);
80   
81   SignalProxy *p = signalProxy();
82   p->attachSignal(this, SIGNAL(sendSessionData(const QString &, const QVariant &)),
83                   SIGNAL(clientSessionDataChanged(const QString &, const QVariant &)));
84   p->attachSlot(SIGNAL(coreSessionDataChanged(const QString &, const QVariant &)),
85                 this, SLOT(recvSessionData(const QString &, const QVariant &)));
86   p->attachSlot(SIGNAL(coreState(const QVariant &)),
87                 this, SLOT(recvCoreState(const QVariant &)));
88   p->attachSlot(SIGNAL(networkConnected(uint)),
89                 this, SLOT(networkConnected(uint)));
90   p->attachSlot(SIGNAL(networkDisconnected(uint)),
91                 this, SLOT(networkDisconnected(uint)));
92   p->attachSlot(SIGNAL(displayMsg(const Message &)),
93                 this, SLOT(recvMessage(const Message &)));
94   p->attachSlot(SIGNAL(displayStatusMsg(QString, QString)),
95                 this, SLOT(recvStatusMsg(QString, QString)));
96
97
98   p->attachSlot(SIGNAL(backlogData(BufferInfo, const QVariantList &, bool)), this, SLOT(recvBacklogData(BufferInfo, const QVariantList &, bool)));
99   p->attachSlot(SIGNAL(bufferInfoUpdated(BufferInfo)), this, SLOT(updateBufferInfo(BufferInfo)));
100   p->attachSignal(this, SIGNAL(sendInput(BufferInfo, QString)));
101   p->attachSignal(this, SIGNAL(requestNetworkStates()));
102
103   p->attachSignal(this, SIGNAL(requestCreateIdentity(const Identity &)), SIGNAL(createIdentity(const Identity &)));
104   p->attachSignal(this, SIGNAL(requestUpdateIdentity(const Identity &)), SIGNAL(updateIdentity(const Identity &)));
105   p->attachSignal(this, SIGNAL(requestRemoveIdentity(IdentityId)), SIGNAL(removeIdentity(IdentityId)));
106   p->attachSlot(SIGNAL(identityCreated(const Identity &)), this, SLOT(coreIdentityCreated(const Identity &)));
107   p->attachSlot(SIGNAL(identityRemoved(IdentityId)), this, SLOT(coreIdentityRemoved(IdentityId)));
108
109   connect(mainUi, SIGNAL(connectToCore(const QVariantMap &)), this, SLOT(connectToCore(const QVariantMap &)));
110   connect(mainUi, SIGNAL(disconnectFromCore()), this, SLOT(disconnectFromCore()));
111   connect(this, SIGNAL(connected()), mainUi, SLOT(connectedToCore()));
112   connect(this, SIGNAL(disconnected()), mainUi, SLOT(disconnectedFromCore()));
113
114   layoutTimer = new QTimer(this);
115   layoutTimer->setInterval(0);
116   layoutTimer->setSingleShot(false);
117   connect(layoutTimer, SIGNAL(timeout()), this, SLOT(layoutMsg()));
118
119 }
120
121 /*** public static methods ***/
122
123
124 QList<Network *> Client::networks() {
125   return instance()->_network.values();
126 }
127
128 Network *Client::network(uint networkid) {
129   if(instance()->_network.contains(networkid))
130     return instance()->_network[networkid];
131   else
132     return 0;
133 }
134
135 QList<BufferInfo> Client::allBufferInfos() {
136   QList<BufferInfo> bufferids;
137   foreach(Buffer *buffer, buffers()) {
138     bufferids << buffer->bufferInfo();
139   }
140   return bufferids;
141 }
142
143 QList<Buffer *> Client::buffers() {
144   return instance()->_buffers.values();
145 }
146
147 Buffer *Client::buffer(uint bufferUid) {
148   if(instance()->_buffers.contains(bufferUid))
149     return instance()->_buffers[bufferUid];
150   else
151     return 0;
152 }
153
154 Buffer *Client::buffer(BufferInfo id) {
155   Buffer *buff = buffer(id.uid());
156
157   if(!buff) {
158     Client *client = Client::instance();
159     buff = new Buffer(id, client);
160
161     connect(buff, SIGNAL(userInput(BufferInfo, QString)),
162             client, SLOT(userInput(BufferInfo, QString)));
163     connect(buff, SIGNAL(destroyed()),
164             client, SLOT(bufferDestroyed()));
165     client->_buffers[id.uid()] = buff;
166     emit client->bufferUpdated(id);
167   }
168   Q_ASSERT(buff);
169   return buff;
170 }
171
172 NetworkModel *Client::networkModel() {
173   return instance()->_networkModel;
174 }
175
176 BufferModel *Client::bufferModel() {
177   return instance()->_bufferModel;
178 }
179
180 NickModel *Client::nickModel() {
181   return instance()->_nickModel;
182 }
183
184
185 SignalProxy *Client::signalProxy() {
186   return instance()->_signalProxy;
187 }
188
189
190 /*** Identity handling ***/
191
192 QList<IdentityId> Client::identityIds() {
193   return instance()->_identities.keys();
194 }
195
196 const Identity * Client::identity(IdentityId id) {
197   if(instance()->_identities.contains(id)) return instance()->_identities[id];
198   else return 0;
199 }
200
201
202 void Client::createIdentity(const Identity &id) {
203   emit instance()->requestCreateIdentity(id);
204 }
205
206 void Client::updateIdentity(const Identity &id) {
207   emit instance()->requestUpdateIdentity(id);
208 }
209
210 void Client::removeIdentity(IdentityId id) {
211   emit instance()->requestRemoveIdentity(id);
212 }
213
214 void Client::coreIdentityCreated(const Identity &other) {
215   if(!_identities.contains(other.id())) {
216     Identity *identity = new Identity(other, this);
217     _identities[other.id()] = identity;
218     identity->setInitialized();
219     signalProxy()->synchronize(identity);
220     emit identityCreated(other.id());
221   } else {
222     qWarning() << tr("Identity already exists in client!");
223   }
224 }
225
226 void Client::coreIdentityRemoved(IdentityId id) {
227   if(_identities.contains(id)) {
228     emit identityRemoved(id);
229     Identity *i = _identities.take(id);
230     i->deleteLater();
231   }
232 }
233
234 /***  ***/
235
236
237 bool Client::isConnected() {
238   return instance()->connectedToCore;
239 }
240
241 void Client::fakeInput(uint bufferUid, QString message) {
242   Buffer *buff = buffer(bufferUid);
243   if(!buff)
244     qWarning() << "No Buffer with uid" << bufferUid << "can't send Input" << message;
245   else
246     emit instance()->sendInput(buff->bufferInfo(), message);
247 }
248
249 void Client::fakeInput(BufferInfo bufferInfo, QString message) {
250   fakeInput(bufferInfo, message);
251 }
252
253 void Client::connectToCore(const QVariantMap &conn) {
254   // TODO implement SSL
255   coreConnectionInfo = conn;
256   if(isConnected()) {
257     emit coreConnectionError(tr("Already connected to Core!"));
258     return;
259   }
260   
261   if(socket != 0)
262     socket->deleteLater();
263   
264   if(conn["Host"].toString().isEmpty()) {
265     clientMode = LocalCore;
266     socket = new QBuffer(this);
267     connect(socket, SIGNAL(readyRead()), this, SLOT(coreHasData()));
268     socket->open(QIODevice::ReadWrite);
269     //QVariant state = connectToLocalCore(coreConnectionInfo["User"].toString(), coreConnectionInfo["Password"].toString());
270     //syncToCore(state);
271     coreSocketConnected();
272   } else {
273     clientMode = RemoteCore;
274     emit coreConnectionMsg(tr("Connecting..."));
275     Q_ASSERT(!socket);
276     QTcpSocket *sock = new QTcpSocket(this);
277     socket = sock;
278     connect(sock, SIGNAL(readyRead()), this, SLOT(coreHasData()));
279     connect(sock, SIGNAL(connected()), this, SLOT(coreSocketConnected()));
280     connect(signalProxy(), SIGNAL(disconnected()), this, SLOT(coreSocketDisconnected()));
281     connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(coreSocketError(QAbstractSocket::SocketError)));
282     sock->connectToHost(conn["Host"].toString(), conn["Port"].toUInt());
283   }
284 }
285
286 void Client::disconnectFromCore() {
287   socket->close();
288 }
289
290 void Client::setCoreConfiguration(const QVariantMap &settings) {
291   SignalProxy::writeDataToDevice(socket, settings);
292 }
293
294 void Client::coreSocketConnected() {
295   connect(this, SIGNAL(recvPartialItem(uint, uint)), this, SIGNAL(coreConnectionProgress(uint, uint)));
296   emit coreConnectionMsg(tr("Synchronizing to core..."));
297   QVariantMap clientInit;
298   clientInit["GuiProtocol"] = GUI_PROTOCOL;
299   clientInit["User"] = coreConnectionInfo["User"].toString();
300   clientInit["Password"] = coreConnectionInfo["Password"].toString();
301   SignalProxy::writeDataToDevice(socket, clientInit);
302 }
303
304 void Client::coreSocketDisconnected() {
305   instance()->connectedToCore = false;
306   emit disconnected();
307   emit coreConnectionStateChanged(false);
308   socket->deleteLater();
309   blockSize = 0;
310
311   /* Clear internal data. Hopefully nothing relies on it at this point. */
312   _networkModel->clear();
313
314   QHash<BufferId, Buffer *>::iterator bufferIter =  _buffers.begin();
315   while(bufferIter != _buffers.end()) {
316     Buffer *buffer = bufferIter.value();
317     disconnect(buffer, SIGNAL(destroyed()), this, 0);
318     bufferIter = _buffers.erase(bufferIter);
319     buffer->deleteLater();
320   }
321   Q_ASSERT(_buffers.isEmpty());
322
323
324   QHash<NetworkId, Network*>::iterator netIter = _network.begin();
325   while(netIter != _network.end()) {
326     Network *net = netIter.value();
327     disconnect(net, SIGNAL(destroyed()), this, 0);
328     netIter = _network.erase(netIter);
329     net->deleteLater();
330   }
331   Q_ASSERT(_network.isEmpty());
332
333   QHash<IdentityId, Identity*>::iterator idIter = _identities.begin();
334   while(idIter != _identities.end()) {
335     Identity *id = idIter.value();
336     emit identityRemoved(id->id());
337     idIter = _identities.erase(idIter);
338     id->deleteLater();
339   }
340   Q_ASSERT(_identities.isEmpty());
341
342   coreConnectionInfo.clear();
343   sessionData.clear();
344   layoutQueue.clear();
345   layoutTimer->stop();
346 }
347
348 void Client::recvCoreState(const QVariant &state) {
349   disconnect(this, SIGNAL(recvPartialItem(uint, uint)), this, SIGNAL(coreConnectionProgress(uint, uint)));
350   disconnect(socket, 0, this, 0);  // rest of communication happens through SignalProxy
351   signalProxy()->addPeer(socket);
352   syncToCore(state);
353 }
354
355 // TODO: auth errors
356 void Client::syncToCore(const QVariant &coreState) {
357   if(!coreState.toMap().contains("SessionState")) {
358     emit coreConnectionError(tr("Invalid data received from core!"));
359     disconnectFromCore();
360     return;
361   }
362
363   QVariantMap sessionState = coreState.toMap()["SessionState"].toMap();
364
365   // store sessionData
366   QVariantMap sessData = sessionState["SessionData"].toMap();
367   foreach(QString key, sessData.keys())
368     recvSessionData(key, sessData[key]);
369
370   // create identities
371   foreach(QVariant vid, sessionState["Identities"].toList()) {
372     coreIdentityCreated(vid.value<Identity>());
373   }
374
375   // store Buffer details
376   QVariantList coreBuffers = sessionState["Buffers"].toList();
377   /* make lookups by id faster */
378   foreach(QVariant vid, coreBuffers) {
379     buffer(vid.value<BufferInfo>()); // create all buffers, so we see them in the network views
380   }
381
382   // create network objects
383   QVariantList networkids = sessionState["Networks"].toList();
384   foreach(QVariant networkid, networkids) {
385     networkConnected(networkid.toUInt());
386   }
387
388   instance()->connectedToCore = true;
389   updateCoreConnectionProgress();
390
391 }
392
393 void Client::updateCoreConnectionProgress() {
394   // we'll do this in three steps:
395   // 1.) networks
396   // 2.) channels
397   // 3.) ircusers
398
399   int numNets = networks().count();
400   int numNetsWaiting = 0;
401
402   int numIrcUsers = 0;
403   int numIrcUsersWaiting = 0;
404
405   int numChannels = 0;
406   int numChannelsWaiting = 0;
407
408   foreach(Network *net, networks()) {
409     if(! net->initialized())
410       numNetsWaiting++;
411
412     numIrcUsers += net->ircUsers().count();
413     foreach(IrcUser *user, net->ircUsers()) {
414       if(! user->initialized())
415         numIrcUsersWaiting++;
416     }
417
418     numChannels += net->ircChannels().count();
419     foreach(IrcChannel *channel, net->ircChannels()) {
420       if(! channel->initialized())
421         numChannelsWaiting++;
422     }
423   }
424
425   if(numNetsWaiting > 0) {
426     emit coreConnectionMsg(tr("Requesting network states..."));
427     emit coreConnectionProgress(numNets - numNetsWaiting, numNets);
428     return;
429   }
430
431   if(numIrcUsersWaiting > 0) {
432     emit coreConnectionMsg(tr("Requesting User states..."));
433     emit coreConnectionProgress(numIrcUsers - numIrcUsersWaiting, numIrcUsers);
434     return;
435   }
436
437   if(numChannelsWaiting > 0) {
438     emit coreConnectionMsg(tr("Requesting Channel states..."));
439     emit coreConnectionProgress(numChannels - numChannelsWaiting, numChannels);
440     return;
441   }
442
443   emit coreConnectionProgress(1,1);
444   emit connected();
445   emit coreConnectionStateChanged(true);
446   foreach(Network *net, networks()) {
447     disconnect(net, 0, this, SLOT(updateCoreConnectionProgress()));
448   }
449
450   // signalProxy()->dumpProxyStats();
451 }
452
453 void Client::recvSessionData(const QString &key, const QVariant &data) {
454   sessionData[key] = data;
455   emit sessionDataChanged(key, data);
456   emit sessionDataChanged(key);
457 }
458
459 void Client::storeSessionData(const QString &key, const QVariant &data) {
460   // Not sure if this is a good idea, but we'll try it anyway:
461   // Calling this function only sends a signal to core. Data is stored upon reception of the update signal,
462   // rather than immediately.
463   emit instance()->sendSessionData(key, data);
464 }
465
466 QVariant Client::retrieveSessionData(const QString &key, const QVariant &def) {
467   if(instance()->sessionData.contains(key)) return instance()->sessionData[key];
468   else return def;
469 }
470
471 QStringList Client::sessionDataKeys() {
472   return instance()->sessionData.keys();
473 }
474
475 void Client::coreSocketError(QAbstractSocket::SocketError) {
476   emit coreConnectionError(socket->errorString());
477   socket->deleteLater();
478 }
479
480 void Client::coreHasData() {
481   QVariant item;
482   if(SignalProxy::readDataFromDevice(socket, blockSize, item)) {
483     emit recvPartialItem(1,1);
484     QVariantMap msg = item.toMap();
485     if (!msg["StartWizard"].toBool()) {
486       recvCoreState(msg["Reply"]);
487     } else {
488       qWarning("Core not configured!");
489       qDebug() << "Available storage providers: " << msg["StorageProviders"].toStringList();
490       emit showConfigWizard(msg);
491     }
492     blockSize = 0;
493     return;
494   }
495   if(blockSize > 0) {
496     emit recvPartialItem(socket->bytesAvailable(), blockSize);
497   }
498 }
499
500 void Client::networkConnected(uint netid) {
501   // TODO: create statusBuffer / switch to networkids
502   //BufferInfo id = statusBufferInfo(net);
503   //Buffer *b = buffer(id);
504   //b->setActive(true);
505
506   Network *netinfo = new Network(netid, this);
507   netinfo->setProxy(signalProxy());
508   networkModel()->attachNetwork(netinfo);
509   
510   if(!isConnected()) {
511     connect(netinfo, SIGNAL(initDone()), this, SLOT(updateCoreConnectionProgress()));
512     connect(netinfo, SIGNAL(ircUserInitDone()), this, SLOT(updateCoreConnectionProgress()));
513     connect(netinfo, SIGNAL(ircChannelInitDone()), this, SLOT(updateCoreConnectionProgress()));
514   }
515   connect(netinfo, SIGNAL(destroyed()), this, SLOT(networkDestroyed()));
516   _network[netid] = netinfo;
517 }
518
519 void Client::networkDisconnected(uint networkid) {
520   if(!_network.contains(networkid)) {
521     qWarning() << "Client::networkDisconnected(uint): unknown Network" << networkid;
522     return;
523   }
524
525   Network *net = _network.take(networkid);
526   if(!net->initialized()) {
527     qDebug() << "Network" << networkid << "disconnected while not yet initialized!";
528     updateCoreConnectionProgress();
529   }
530   net->deleteLater();
531 }
532
533 void Client::updateBufferInfo(BufferInfo id) {
534   emit bufferUpdated(id);
535 }
536
537 void Client::bufferDestroyed() {
538   Buffer *buffer = static_cast<Buffer *>(sender());
539   QHash<BufferId, Buffer *>::iterator iter = _buffers.begin();
540   while(iter != _buffers.end()) {
541     if(iter.value() == buffer) {
542       iter = _buffers.erase(iter);
543       break;
544     }
545     iter++;
546   }
547 }
548
549 void Client::networkDestroyed() {
550   Network *netinfo = static_cast<Network *>(sender());
551   uint networkId = netinfo->networkId();
552   if(_network.contains(networkId))
553     _network.remove(networkId);
554 }
555
556 void Client::recvMessage(const Message &msg) {
557   Buffer *b = buffer(msg.buffer());
558
559 //   Buffer::ActivityLevel level = Buffer::OtherActivity;
560 //   if(msg.type() == Message::Plain || msg.type() == Message::Notice){
561 //     level |= Buffer::NewMessage;
562 //   }
563 //   if(msg.flags() & Message::Highlight){
564 //     level |= Buffer::Highlight;
565 //   }
566 //   emit bufferActivity(level, b);
567
568   b->appendMsg(msg);
569 }
570
571 void Client::recvStatusMsg(QString /*net*/, QString /*msg*/) {
572   //recvMessage(net, Message::server("", QString("[STATUS] %1").arg(msg)));
573 }
574
575 void Client::recvBacklogData(BufferInfo id, QVariantList msgs, bool /*done*/) {
576   Buffer *b = buffer(id);
577   foreach(QVariant v, msgs) {
578     Message msg = v.value<Message>();
579     b->prependMsg(msg);
580     if(!layoutQueue.contains(b)) layoutQueue.append(b);
581   }
582   if(layoutQueue.count() && !layoutTimer->isActive()) layoutTimer->start();
583 }
584
585 void Client::layoutMsg() {
586   if(layoutQueue.count()) {
587     Buffer *b = layoutQueue.takeFirst();  // TODO make this the current buffer
588     if(b->layoutMsg())
589       layoutQueue.append(b);  // Buffer has more messages in its queue --> Round Robin
590   }
591   
592   if(!layoutQueue.count())
593     layoutTimer->stop();
594 }
595
596 AbstractUiMsg *Client::layoutMsg(const Message &msg) {
597   return instance()->mainUi->layoutMsg(msg);
598 }
599
600 void Client::userInput(BufferInfo id, QString msg) {
601   emit sendInput(id, msg);
602 }
603