Handle client state a bit more sanely
[quassel.git] / src / client / client.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-09 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 "abstractmessageprocessor.h"
24 #include "abstractui.h"
25 #include "bufferinfo.h"
26 #include "buffermodel.h"
27 #include "buffersettings.h"
28 #include "buffersyncer.h"
29 #include "bufferviewconfig.h"
30 #include "bufferviewoverlay.h"
31 #include "clientaliasmanager.h"
32 #include "clientbacklogmanager.h"
33 #include "clientbufferviewmanager.h"
34 #include "clientirclisthelper.h"
35 #include "clientidentity.h"
36 #include "clientignorelistmanager.h"
37 #include "clientuserinputhandler.h"
38 #include "coreaccountmodel.h"
39 #include "coreconnection.h"
40 #include "ircchannel.h"
41 #include "ircuser.h"
42 #include "message.h"
43 #include "messagemodel.h"
44 #include "network.h"
45 #include "networkconfig.h"
46 #include "networkmodel.h"
47 #include "quassel.h"
48 #include "signalproxy.h"
49 #include "util.h"
50
51 #include <stdio.h>
52 #include <stdlib.h>
53
54 QPointer<Client> Client::instanceptr = 0;
55
56 /*** Initialization/destruction ***/
57
58 bool Client::instanceExists()
59 {
60   return instanceptr;
61 }
62
63 Client *Client::instance() {
64   if(!instanceptr)
65     instanceptr = new Client();
66   return instanceptr;
67 }
68
69 void Client::destroy() {
70   if(instanceptr) {
71     delete instanceptr->mainUi();
72     instanceptr->deleteLater();
73     instanceptr = 0;
74   }
75 }
76
77 void Client::init(AbstractUi *ui) {
78   instance()->_mainUi = ui;
79   instance()->init();
80 }
81
82 Client::Client(QObject *parent)
83   : QObject(parent),
84     _signalProxy(new SignalProxy(SignalProxy::Client, this)),
85     _mainUi(0),
86     _networkModel(0),
87     _bufferModel(0),
88     _bufferSyncer(0),
89     _aliasManager(0),
90     _backlogManager(new ClientBacklogManager(this)),
91     _bufferViewManager(0),
92     _bufferViewOverlay(new BufferViewOverlay(this)),
93     _ircListHelper(new ClientIrcListHelper(this)),
94     _inputHandler(0),
95     _networkConfig(0),
96     _ignoreListManager(0),
97     _messageModel(0),
98     _messageProcessor(0),
99     _coreAccountModel(new CoreAccountModel(this)),
100     _coreConnection(new CoreConnection(_coreAccountModel, this)),
101     _connected(false),
102     _debugLog(&_debugLogBuffer)
103 {
104   _signalProxy->synchronize(_ircListHelper);
105 }
106
107 Client::~Client() {
108   disconnectFromCore();
109 }
110
111 void Client::init() {
112   _networkModel = new NetworkModel(this);
113
114   connect(this, SIGNAL(networkRemoved(NetworkId)),
115           _networkModel, SLOT(networkRemoved(NetworkId)));
116
117   _bufferModel = new BufferModel(_networkModel);
118   _messageModel = mainUi()->createMessageModel(this);
119   _messageProcessor = mainUi()->createMessageProcessor(this);
120   _inputHandler = new ClientUserInputHandler(this);
121
122   SignalProxy *p = signalProxy();
123
124   p->attachSlot(SIGNAL(displayMsg(const Message &)), this, SLOT(recvMessage(const Message &)));
125   p->attachSlot(SIGNAL(displayStatusMsg(QString, QString)), this, SLOT(recvStatusMsg(QString, QString)));
126
127   p->attachSlot(SIGNAL(bufferInfoUpdated(BufferInfo)), _networkModel, SLOT(bufferUpdated(BufferInfo)));
128   p->attachSignal(inputHandler(), SIGNAL(sendInput(BufferInfo, QString)));
129   p->attachSignal(this, SIGNAL(requestNetworkStates()));
130
131   p->attachSignal(this, SIGNAL(requestCreateIdentity(const Identity &, const QVariantMap &)), SIGNAL(createIdentity(const Identity &, const QVariantMap &)));
132   p->attachSignal(this, SIGNAL(requestRemoveIdentity(IdentityId)), SIGNAL(removeIdentity(IdentityId)));
133   p->attachSlot(SIGNAL(identityCreated(const Identity &)), this, SLOT(coreIdentityCreated(const Identity &)));
134   p->attachSlot(SIGNAL(identityRemoved(IdentityId)), this, SLOT(coreIdentityRemoved(IdentityId)));
135
136   p->attachSignal(this, SIGNAL(requestCreateNetwork(const NetworkInfo &, const QStringList &)), SIGNAL(createNetwork(const NetworkInfo &, const QStringList &)));
137   p->attachSignal(this, SIGNAL(requestRemoveNetwork(NetworkId)), SIGNAL(removeNetwork(NetworkId)));
138   p->attachSlot(SIGNAL(networkCreated(NetworkId)), this, SLOT(coreNetworkCreated(NetworkId)));
139   p->attachSlot(SIGNAL(networkRemoved(NetworkId)), this, SLOT(coreNetworkRemoved(NetworkId)));
140
141   //connect(mainUi(), SIGNAL(connectToCore(const QVariantMap &)), this, SLOT(connectToCore(const QVariantMap &)));
142   connect(mainUi(), SIGNAL(disconnectFromCore()), this, SLOT(disconnectFromCore()));
143   connect(this, SIGNAL(connected()), mainUi(), SLOT(connectedToCore()));
144   connect(this, SIGNAL(disconnected()), mainUi(), SLOT(disconnectedFromCore()));
145
146   // attach backlog manager
147   p->synchronize(backlogManager());
148   connect(backlogManager(), SIGNAL(messagesReceived(BufferId, int)), _messageModel, SLOT(messagesReceived(BufferId, int)));
149
150   coreAccountModel()->load();
151
152   connect(coreConnection(), SIGNAL(stateChanged(CoreConnection::ConnectionState)), SLOT(connectionStateChanged(CoreConnection::ConnectionState)));
153   coreConnection()->init();
154 }
155
156 /*** public static methods ***/
157
158 AbstractUi *Client::mainUi() {
159   return instance()->_mainUi;
160 }
161
162 bool Client::isConnected() {
163   return instance()->_connected;
164 }
165
166 bool Client::internalCore() {
167   return currentCoreAccount().isInternal();
168 }
169
170 /*** Network handling ***/
171
172 QList<NetworkId> Client::networkIds() {
173   return instance()->_networks.keys();
174 }
175
176 const Network * Client::network(NetworkId networkid) {
177   if(instance()->_networks.contains(networkid)) return instance()->_networks[networkid];
178   else return 0;
179 }
180
181 void Client::createNetwork(const NetworkInfo &info, const QStringList &persistentChannels) {
182   emit instance()->requestCreateNetwork(info, persistentChannels);
183 }
184
185 void Client::removeNetwork(NetworkId id) {
186   emit instance()->requestRemoveNetwork(id);
187 }
188
189 void Client::updateNetwork(const NetworkInfo &info) {
190   Network *netptr = instance()->_networks.value(info.networkId, 0);
191   if(!netptr) {
192     qWarning() << "Update for unknown network requested:" << info;
193     return;
194   }
195   netptr->requestSetNetworkInfo(info);
196 }
197
198 void Client::addNetwork(Network *net) {
199   net->setProxy(signalProxy());
200   signalProxy()->synchronize(net);
201   networkModel()->attachNetwork(net);
202   connect(net, SIGNAL(destroyed()), instance(), SLOT(networkDestroyed()));
203   instance()->_networks[net->networkId()] = net;
204   emit instance()->networkCreated(net->networkId());
205 }
206
207 void Client::coreNetworkCreated(NetworkId id) {
208   if(_networks.contains(id)) {
209     qWarning() << "Creation of already existing network requested!";
210     return;
211   }
212   Network *net = new Network(id, this);
213   addNetwork(net);
214 }
215
216 void Client::coreNetworkRemoved(NetworkId id) {
217   if(!_networks.contains(id))
218     return;
219   Network *net = _networks.take(id);
220   emit networkRemoved(net->networkId());
221   net->deleteLater();
222 }
223
224 /*** Identity handling ***/
225
226 QList<IdentityId> Client::identityIds() {
227   return instance()->_identities.keys();
228 }
229
230 const Identity *Client::identity(IdentityId id) {
231   if(instance()->_identities.contains(id)) return instance()->_identities[id];
232   else return 0;
233 }
234
235 void Client::createIdentity(const CertIdentity &id) {
236   QVariantMap additional;
237 #ifdef HAVE_SSL
238   additional["KeyPem"] = id.sslKey().toPem();
239   additional["CertPem"] = id.sslCert().toPem();
240 #endif
241   emit instance()->requestCreateIdentity(id, additional);
242 }
243
244 void Client::updateIdentity(IdentityId id, const QVariantMap &ser) {
245   Identity *idptr = instance()->_identities.value(id, 0);
246   if(!idptr) {
247     qWarning() << "Update for unknown identity requested:" << id;
248     return;
249   }
250   idptr->requestUpdate(ser);
251 }
252
253 void Client::removeIdentity(IdentityId id) {
254   emit instance()->requestRemoveIdentity(id);
255 }
256
257 void Client::coreIdentityCreated(const Identity &other) {
258   if(!_identities.contains(other.id())) {
259     Identity *identity = new Identity(other, this);
260     _identities[other.id()] = identity;
261     identity->setInitialized();
262     signalProxy()->synchronize(identity);
263     emit identityCreated(other.id());
264   } else {
265     qWarning() << tr("Identity already exists in client!");
266   }
267 }
268
269 void Client::coreIdentityRemoved(IdentityId id) {
270   if(_identities.contains(id)) {
271     emit identityRemoved(id);
272     Identity *i = _identities.take(id);
273     i->deleteLater();
274   }
275 }
276
277 /*** User input handling ***/
278
279 void Client::userInput(const BufferInfo &bufferInfo, const QString &message) {
280   // we need to make sure that AliasManager is ready before processing input
281   if(aliasManager() && aliasManager()->isInitialized())
282     inputHandler()->handleUserInput(bufferInfo, message);
283   else
284    instance()-> _userInputBuffer.append(qMakePair(bufferInfo, message));
285 }
286
287 void Client::sendBufferedUserInput() {
288   for(int i = 0; i < _userInputBuffer.count(); i++)
289     userInput(_userInputBuffer.at(i).first, _userInputBuffer.at(i).second);
290
291   _userInputBuffer.clear();
292 }
293
294 /*** core connection stuff ***/
295
296 void Client::connectionStateChanged(CoreConnection::ConnectionState state) {
297   switch(state) {
298   case CoreConnection::Disconnected:
299     setDisconnectedFromCore();
300     break;
301   case CoreConnection::Synchronized:
302     setSyncedToCore();
303     break;
304   default:
305     break;
306   }
307 }
308
309 void Client::setSyncedToCore() {
310   // create buffersyncer
311   Q_ASSERT(!_bufferSyncer);
312   _bufferSyncer = new BufferSyncer(this);
313   connect(bufferSyncer(), SIGNAL(lastSeenMsgSet(BufferId, MsgId)), _networkModel, SLOT(setLastSeenMsgId(BufferId, MsgId)));
314   connect(bufferSyncer(), SIGNAL(bufferRemoved(BufferId)), this, SLOT(bufferRemoved(BufferId)));
315   connect(bufferSyncer(), SIGNAL(bufferRenamed(BufferId, QString)), this, SLOT(bufferRenamed(BufferId, QString)));
316   connect(bufferSyncer(), SIGNAL(buffersPermanentlyMerged(BufferId, BufferId)), this, SLOT(buffersPermanentlyMerged(BufferId, BufferId)));
317   connect(bufferSyncer(), SIGNAL(buffersPermanentlyMerged(BufferId, BufferId)), _messageModel, SLOT(buffersPermanentlyMerged(BufferId, BufferId)));
318   connect(networkModel(), SIGNAL(setLastSeenMsg(BufferId, MsgId)), bufferSyncer(), SLOT(requestSetLastSeenMsg(BufferId, const MsgId &)));
319   signalProxy()->synchronize(bufferSyncer());
320
321   // create a new BufferViewManager
322   Q_ASSERT(!_bufferViewManager);
323   _bufferViewManager = new ClientBufferViewManager(signalProxy(), this);
324   connect(bufferViewManager(), SIGNAL(initDone()), this, SLOT(createDefaultBufferView()));
325
326   // create AliasManager
327   Q_ASSERT(!_aliasManager);
328   _aliasManager = new ClientAliasManager(this);
329   connect(aliasManager(), SIGNAL(initDone()), SLOT(sendBufferedUserInput()));
330   signalProxy()->synchronize(aliasManager());
331
332   // create NetworkConfig
333   Q_ASSERT(!_networkConfig);
334   _networkConfig = new NetworkConfig("GlobalNetworkConfig", this);
335   signalProxy()->synchronize(networkConfig());
336
337   // create IgnoreListManager
338   Q_ASSERT(!_ignoreListManager);
339   _ignoreListManager = new ClientIgnoreListManager(this);
340   signalProxy()->synchronize(ignoreListManager());
341
342   // trigger backlog request once all active bufferviews are initialized
343   connect(bufferViewOverlay(), SIGNAL(initDone()), this, SLOT(requestInitialBacklog()));
344
345   _connected = true;
346   emit connected();
347   emit coreConnectionStateChanged(true);
348 }
349
350 void Client::requestInitialBacklog() {
351   // usually it _should_ take longer until the bufferViews are initialized, so that's what
352   // triggers this slot. But we have to make sure that we know all buffers yet.
353   // so we check the BufferSyncer and in case it wasn't initialized we wait for that instead
354   if(!bufferSyncer()->isInitialized()) {
355     connect(bufferViewOverlay(), SIGNAL(initDone()), this, SLOT(requestInitialBacklog()));
356     connect(bufferSyncer(), SIGNAL(initDone()), this, SLOT(requestInitialBacklog()));
357     return;
358   }
359   _backlogManager->requestInitialBacklog();
360 }
361
362 void Client::createDefaultBufferView() {
363   if(bufferViewManager()->bufferViewConfigs().isEmpty()) {
364     BufferViewConfig config(-1);
365     config.setBufferViewName(tr("All Chats"));
366     config.initSetBufferList(networkModel()->allBufferIdsSorted());
367     bufferViewManager()->requestCreateBufferView(config.toVariantMap());
368   }
369 }
370
371 void Client::disconnectFromCore() {
372   if(!coreConnection()->isConnected())
373     return;
374
375   coreConnection()->disconnectFromCore();
376 }
377
378 void Client::setDisconnectedFromCore() {
379   _connected = false;
380   emit disconnected();
381   emit coreConnectionStateChanged(false);
382
383   backlogManager()->reset();
384   messageProcessor()->reset();
385
386   // Clear internal data. Hopefully nothing relies on it at this point.
387
388   if(_bufferSyncer) {
389     _bufferSyncer->deleteLater();
390     _bufferSyncer = 0;
391   }
392
393   if(_bufferViewManager) {
394     _bufferViewManager->deleteLater();
395     _bufferViewManager = 0;
396   }
397
398   if(_aliasManager) {
399     _aliasManager->deleteLater();
400     _aliasManager = 0;
401   }
402
403   if(_ignoreListManager) {
404     _ignoreListManager->deleteLater();
405     _ignoreListManager = 0;
406   }
407   // we probably don't want to save pending input for reconnect
408   _userInputBuffer.clear();
409
410   _messageModel->clear();
411   _networkModel->clear();
412
413   QHash<NetworkId, Network*>::iterator netIter = _networks.begin();
414   while(netIter != _networks.end()) {
415     Network *net = netIter.value();
416     emit networkRemoved(net->networkId());
417     disconnect(net, SIGNAL(destroyed()), this, 0);
418     netIter = _networks.erase(netIter);
419     net->deleteLater();
420   }
421   Q_ASSERT(_networks.isEmpty());
422
423   QHash<IdentityId, Identity *>::iterator idIter = _identities.begin();
424   while(idIter != _identities.end()) {
425     emit identityRemoved(idIter.key());
426     Identity *id = idIter.value();
427     idIter = _identities.erase(idIter);
428     id->deleteLater();
429   }
430   Q_ASSERT(_identities.isEmpty());
431
432   if(_networkConfig) {
433     _networkConfig->deleteLater();
434     _networkConfig = 0;
435   }
436 }
437
438 /*** ***/
439
440 void Client::networkDestroyed() {
441   Network *net = static_cast<Network *>(sender());
442   QHash<NetworkId, Network *>::iterator netIter = _networks.begin();
443   while(netIter != _networks.end()) {
444     if(*netIter == net) {
445       netIter = _networks.erase(netIter);
446       break;
447     } else {
448       netIter++;
449     }
450   }
451 }
452
453 // Hmm... we never used this...
454 void Client::recvStatusMsg(QString /*net*/, QString /*msg*/) {
455   //recvMessage(net, Message::server("", QString("[STATUS] %1").arg(msg)));
456 }
457
458 void Client::recvMessage(const Message &msg) {
459   Message msg_ = msg;
460   messageProcessor()->process(msg_);
461 }
462
463 void Client::setBufferLastSeenMsg(BufferId id, const MsgId &msgId) {
464   if(!bufferSyncer())
465     return;
466   bufferSyncer()->requestSetLastSeenMsg(id, msgId);
467 }
468
469 void Client::removeBuffer(BufferId id) {
470   if(!bufferSyncer()) return;
471   bufferSyncer()->requestRemoveBuffer(id);
472 }
473
474 void Client::renameBuffer(BufferId bufferId, const QString &newName) {
475   if(!bufferSyncer())
476     return;
477   bufferSyncer()->requestRenameBuffer(bufferId, newName);
478 }
479
480 void Client::mergeBuffersPermanently(BufferId bufferId1, BufferId bufferId2) {
481   if(!bufferSyncer())
482     return;
483   bufferSyncer()->requestMergeBuffersPermanently(bufferId1, bufferId2);
484 }
485
486 void Client::purgeKnownBufferIds() {
487   if(!bufferSyncer())
488     return;
489   bufferSyncer()->requestPurgeBufferIds();
490 }
491
492 void Client::bufferRemoved(BufferId bufferId) {
493   // select a sane buffer (status buffer)
494   /* we have to manually select a buffer because otherwise inconsitent changes
495    * to the model might occur:
496    * the result of a buffer removal triggers a change in the selection model.
497    * the newly selected buffer might be a channel that hasn't been selected yet
498    * and a new nickview would be created (which never heard of the "rowsAboutToBeRemoved").
499    * this new view (and/or) its sort filter will then only receive a "rowsRemoved" signal.
500    */
501   QModelIndex current = bufferModel()->currentIndex();
502   if(current.data(NetworkModel::BufferIdRole).value<BufferId>() == bufferId) {
503     bufferModel()->setCurrentIndex(current.sibling(0,0));
504   }
505
506   // and remove it from the model
507   networkModel()->removeBuffer(bufferId);
508 }
509
510 void Client::bufferRenamed(BufferId bufferId, const QString &newName) {
511   QModelIndex bufferIndex = networkModel()->bufferIndex(bufferId);
512   if(bufferIndex.isValid()) {
513     networkModel()->setData(bufferIndex, newName, Qt::DisplayRole);
514   }
515 }
516
517 void Client::buffersPermanentlyMerged(BufferId bufferId1, BufferId bufferId2) {
518   QModelIndex idx = networkModel()->bufferIndex(bufferId1);
519   bufferModel()->setCurrentIndex(bufferModel()->mapFromSource(idx));
520   networkModel()->removeBuffer(bufferId2);
521 }
522
523 void Client::logMessage(QtMsgType type, const char *msg) {
524   fprintf(stderr, "%s\n", msg);
525   fflush(stderr);
526   if(type == QtFatalMsg) {
527     Quassel::logFatalMessage(msg);
528   } else {
529     QString msgString = QString("%1\n").arg(msg);
530
531     //Check to see if there is an instance around, else we risk recursions
532     //when calling instance() and creating new ones.
533     if (!instanceExists())
534       return;
535
536     instance()->_debugLog << msgString;
537     emit instance()->logUpdated(msgString);
538   }
539 }