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