client: Detect legacy CoreInfo, request on show
[quassel.git] / src / client / client.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 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  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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 "clienttransfermanager.h"
38 #include "clientuserinputhandler.h"
39 #include "coreaccountmodel.h"
40 #include "coreconnection.h"
41 #include "dccconfig.h"
42 #include "ircchannel.h"
43 #include "ircuser.h"
44 #include "message.h"
45 #include "messagemodel.h"
46 #include "network.h"
47 #include "networkconfig.h"
48 #include "networkmodel.h"
49 #include "quassel.h"
50 #include "signalproxy.h"
51 #include "transfermodel.h"
52 #include "util.h"
53 #include "clientauthhandler.h"
54
55 #include <stdio.h>
56 #include <stdlib.h>
57
58 QPointer<Client> Client::instanceptr = 0;
59
60 /*** Initialization/destruction ***/
61
62 bool Client::instanceExists()
63 {
64     return instanceptr;
65 }
66
67
68 Client *Client::instance()
69 {
70     if (!instanceptr)
71         instanceptr = new Client();
72     return instanceptr;
73 }
74
75
76 void Client::destroy()
77 {
78     if (instanceptr) {
79         delete instanceptr->mainUi();
80         instanceptr->deleteLater();
81         instanceptr = 0;
82     }
83 }
84
85
86 void Client::init(AbstractUi *ui)
87 {
88     instance()->_mainUi = ui;
89     instance()->init();
90 }
91
92
93 Client::Client(QObject *parent)
94     : QObject(parent),
95     _signalProxy(new SignalProxy(SignalProxy::Client, this)),
96     _mainUi(0),
97     _networkModel(0),
98     _bufferModel(0),
99     _bufferSyncer(0),
100     _aliasManager(0),
101     _backlogManager(new ClientBacklogManager(this)),
102     _bufferViewManager(0),
103     _bufferViewOverlay(new BufferViewOverlay(this)),
104     _coreInfo(new CoreInfo(this)),
105     _dccConfig(0),
106     _ircListHelper(new ClientIrcListHelper(this)),
107     _inputHandler(0),
108     _networkConfig(0),
109     _ignoreListManager(0),
110     _highlightRuleManager(0),
111     _transferManager(0),
112     _transferModel(new TransferModel(this)),
113     _messageModel(0),
114     _messageProcessor(0),
115     _coreAccountModel(new CoreAccountModel(this)),
116     _coreConnection(new CoreConnection(this)),
117     _connected(false),
118     _debugLog(&_debugLogBuffer)
119 {
120     _signalProxy->synchronize(_ircListHelper);
121 }
122
123
124 Client::~Client()
125 {
126     disconnectFromCore();
127 }
128
129
130 void Client::init()
131 {
132     _networkModel = new NetworkModel(this);
133
134     connect(this, SIGNAL(networkRemoved(NetworkId)),
135         _networkModel, SLOT(networkRemoved(NetworkId)));
136
137     _bufferModel = new BufferModel(_networkModel);
138     _messageModel = mainUi()->createMessageModel(this);
139     _messageProcessor = mainUi()->createMessageProcessor(this);
140     _inputHandler = new ClientUserInputHandler(this);
141
142     SignalProxy *p = signalProxy();
143
144     p->attachSlot(SIGNAL(displayMsg(const Message &)), this, SLOT(recvMessage(const Message &)));
145     p->attachSlot(SIGNAL(displayStatusMsg(QString, QString)), this, SLOT(recvStatusMsg(QString, QString)));
146
147     p->attachSlot(SIGNAL(bufferInfoUpdated(BufferInfo)), _networkModel, SLOT(bufferUpdated(BufferInfo)));
148     p->attachSignal(inputHandler(), SIGNAL(sendInput(BufferInfo, QString)));
149     p->attachSignal(this, SIGNAL(requestNetworkStates()));
150
151     p->attachSignal(this, SIGNAL(requestCreateIdentity(const Identity &, const QVariantMap &)), SIGNAL(createIdentity(const Identity &, const QVariantMap &)));
152     p->attachSignal(this, SIGNAL(requestRemoveIdentity(IdentityId)), SIGNAL(removeIdentity(IdentityId)));
153     p->attachSlot(SIGNAL(identityCreated(const Identity &)), this, SLOT(coreIdentityCreated(const Identity &)));
154     p->attachSlot(SIGNAL(identityRemoved(IdentityId)), this, SLOT(coreIdentityRemoved(IdentityId)));
155
156     p->attachSignal(this, SIGNAL(requestCreateNetwork(const NetworkInfo &, const QStringList &)), SIGNAL(createNetwork(const NetworkInfo &, const QStringList &)));
157     p->attachSignal(this, SIGNAL(requestRemoveNetwork(NetworkId)), SIGNAL(removeNetwork(NetworkId)));
158     p->attachSlot(SIGNAL(networkCreated(NetworkId)), this, SLOT(coreNetworkCreated(NetworkId)));
159     p->attachSlot(SIGNAL(networkRemoved(NetworkId)), this, SLOT(coreNetworkRemoved(NetworkId)));
160
161     p->attachSignal(this, SIGNAL(requestPasswordChange(PeerPtr,QString,QString,QString)), SIGNAL(changePassword(PeerPtr,QString,QString,QString)));
162     p->attachSlot(SIGNAL(passwordChanged(PeerPtr,bool)), this, SLOT(corePasswordChanged(PeerPtr,bool)));
163
164     p->attachSignal(this, SIGNAL(requestKickClient(int)), SIGNAL(kickClient(int)));
165     p->attachSlot(SIGNAL(disconnectFromCore()), this, SLOT(disconnectFromCore()));
166
167     //connect(mainUi(), SIGNAL(connectToCore(const QVariantMap &)), this, SLOT(connectToCore(const QVariantMap &)));
168     connect(mainUi(), SIGNAL(disconnectFromCore()), this, SLOT(disconnectFromCore()));
169     connect(this, SIGNAL(connected()), mainUi(), SLOT(connectedToCore()));
170     connect(this, SIGNAL(disconnected()), mainUi(), SLOT(disconnectedFromCore()));
171
172     // attach backlog manager
173     p->synchronize(backlogManager());
174     connect(backlogManager(), SIGNAL(messagesReceived(BufferId, int)), _messageModel, SLOT(messagesReceived(BufferId, int)));
175
176     coreAccountModel()->load();
177
178     // Attach CoreInfo
179     p->synchronize(coreInfo());
180
181     connect(coreConnection(), SIGNAL(stateChanged(CoreConnection::ConnectionState)), SLOT(connectionStateChanged(CoreConnection::ConnectionState)));
182     coreConnection()->init();
183 }
184
185
186 /*** public static methods ***/
187
188 AbstractUi *Client::mainUi()
189 {
190     return instance()->_mainUi;
191 }
192
193
194 bool Client::isCoreFeatureEnabled(Quassel::Feature feature)
195 {
196     return coreConnection()->peer() ? coreConnection()->peer()->hasFeature(feature) : false;
197 }
198
199
200 bool Client::isConnected()
201 {
202     return instance()->_connected;
203 }
204
205
206 bool Client::internalCore()
207 {
208     return currentCoreAccount().isInternal();
209 }
210
211
212 void Client::onDbUpgradeInProgress(bool inProgress)
213 {
214     emit dbUpgradeInProgress(inProgress);
215 }
216
217
218 /*** Network handling ***/
219
220 QList<NetworkId> Client::networkIds()
221 {
222     return instance()->_networks.keys();
223 }
224
225
226 const Network *Client::network(NetworkId networkid)
227 {
228     if (instance()->_networks.contains(networkid)) return instance()->_networks[networkid];
229     else return 0;
230 }
231
232
233 void Client::createNetwork(const NetworkInfo &info, const QStringList &persistentChannels)
234 {
235     emit instance()->requestCreateNetwork(info, persistentChannels);
236 }
237
238
239 void Client::removeNetwork(NetworkId id)
240 {
241     emit instance()->requestRemoveNetwork(id);
242 }
243
244
245 void Client::updateNetwork(const NetworkInfo &info)
246 {
247     Network *netptr = instance()->_networks.value(info.networkId, 0);
248     if (!netptr) {
249         qWarning() << "Update for unknown network requested:" << info;
250         return;
251     }
252     netptr->requestSetNetworkInfo(info);
253 }
254
255
256 void Client::addNetwork(Network *net)
257 {
258     net->setProxy(signalProxy());
259     signalProxy()->synchronize(net);
260     networkModel()->attachNetwork(net);
261     connect(net, SIGNAL(destroyed()), instance(), SLOT(networkDestroyed()));
262     instance()->_networks[net->networkId()] = net;
263     emit instance()->networkCreated(net->networkId());
264 }
265
266
267 void Client::coreNetworkCreated(NetworkId id)
268 {
269     if (_networks.contains(id)) {
270         qWarning() << "Creation of already existing network requested!";
271         return;
272     }
273     Network *net = new Network(id, this);
274     addNetwork(net);
275 }
276
277
278 void Client::coreNetworkRemoved(NetworkId id)
279 {
280     if (!_networks.contains(id))
281         return;
282     Network *net = _networks.take(id);
283     emit networkRemoved(net->networkId());
284     net->deleteLater();
285 }
286
287
288 /*** Identity handling ***/
289
290 QList<IdentityId> Client::identityIds()
291 {
292     return instance()->_identities.keys();
293 }
294
295
296 const Identity *Client::identity(IdentityId id)
297 {
298     if (instance()->_identities.contains(id)) return instance()->_identities[id];
299     else return 0;
300 }
301
302
303 void Client::createIdentity(const CertIdentity &id)
304 {
305     QVariantMap additional;
306 #ifdef HAVE_SSL
307     additional["KeyPem"] = id.sslKey().toPem();
308     additional["CertPem"] = id.sslCert().toPem();
309 #endif
310     emit instance()->requestCreateIdentity(id, additional);
311 }
312
313
314 void Client::updateIdentity(IdentityId id, const QVariantMap &ser)
315 {
316     Identity *idptr = instance()->_identities.value(id, 0);
317     if (!idptr) {
318         qWarning() << "Update for unknown identity requested:" << id;
319         return;
320     }
321     idptr->requestUpdate(ser);
322 }
323
324
325 void Client::removeIdentity(IdentityId id)
326 {
327     emit instance()->requestRemoveIdentity(id);
328 }
329
330
331 void Client::coreIdentityCreated(const Identity &other)
332 {
333     if (!_identities.contains(other.id())) {
334         Identity *identity = new Identity(other, this);
335         _identities[other.id()] = identity;
336         identity->setInitialized();
337         signalProxy()->synchronize(identity);
338         emit identityCreated(other.id());
339     }
340     else {
341         qWarning() << tr("Identity already exists in client!");
342     }
343 }
344
345
346 void Client::coreIdentityRemoved(IdentityId id)
347 {
348     if (_identities.contains(id)) {
349         emit identityRemoved(id);
350         Identity *i = _identities.take(id);
351         i->deleteLater();
352     }
353 }
354
355
356 /*** User input handling ***/
357
358 void Client::userInput(const BufferInfo &bufferInfo, const QString &message)
359 {
360     // we need to make sure that AliasManager is ready before processing input
361     if (aliasManager() && aliasManager()->isInitialized())
362         inputHandler()->handleUserInput(bufferInfo, message);
363     else
364         instance()->_userInputBuffer.append(qMakePair(bufferInfo, message));
365 }
366
367
368 void Client::sendBufferedUserInput()
369 {
370     for (int i = 0; i < _userInputBuffer.count(); i++)
371         userInput(_userInputBuffer.at(i).first, _userInputBuffer.at(i).second);
372
373     _userInputBuffer.clear();
374 }
375
376
377 /*** core connection stuff ***/
378
379 void Client::connectionStateChanged(CoreConnection::ConnectionState state)
380 {
381     switch (state) {
382     case CoreConnection::Disconnected:
383         setDisconnectedFromCore();
384         break;
385     case CoreConnection::Synchronized:
386         setSyncedToCore();
387         break;
388     default:
389         break;
390     }
391 }
392
393
394 void Client::setSyncedToCore()
395 {
396     // create buffersyncer
397     Q_ASSERT(!_bufferSyncer);
398     _bufferSyncer = new BufferSyncer(this);
399     connect(bufferSyncer(), SIGNAL(lastSeenMsgSet(BufferId, MsgId)), _networkModel, SLOT(setLastSeenMsgId(BufferId, MsgId)));
400     connect(bufferSyncer(), SIGNAL(markerLineSet(BufferId, MsgId)), _networkModel, SLOT(setMarkerLineMsgId(BufferId, MsgId)));
401     connect(bufferSyncer(), SIGNAL(bufferRemoved(BufferId)), this, SLOT(bufferRemoved(BufferId)));
402     connect(bufferSyncer(), SIGNAL(bufferRenamed(BufferId, QString)), this, SLOT(bufferRenamed(BufferId, QString)));
403     connect(bufferSyncer(), SIGNAL(buffersPermanentlyMerged(BufferId, BufferId)), this, SLOT(buffersPermanentlyMerged(BufferId, BufferId)));
404     connect(bufferSyncer(), SIGNAL(buffersPermanentlyMerged(BufferId, BufferId)), _messageModel, SLOT(buffersPermanentlyMerged(BufferId, BufferId)));
405     connect(bufferSyncer(), SIGNAL(bufferMarkedAsRead(BufferId)), SIGNAL(bufferMarkedAsRead(BufferId)));
406     connect(bufferSyncer(), SIGNAL(bufferActivityChanged(BufferId, const Message::Types)), _networkModel, SLOT(bufferActivityChanged(BufferId, const Message::Types)));
407     connect(bufferSyncer(), SIGNAL(highlightCountChanged(BufferId, int)), _networkModel, SLOT(highlightCountChanged(BufferId, int)));
408     connect(networkModel(), SIGNAL(requestSetLastSeenMsg(BufferId, MsgId)), bufferSyncer(), SLOT(requestSetLastSeenMsg(BufferId, const MsgId &)));
409
410     SignalProxy *p = signalProxy();
411     p->synchronize(bufferSyncer());
412
413     // create a new BufferViewManager
414     Q_ASSERT(!_bufferViewManager);
415     _bufferViewManager = new ClientBufferViewManager(p, this);
416     connect(_bufferViewManager, SIGNAL(initDone()), _bufferViewOverlay, SLOT(restore()));
417
418     // create AliasManager
419     Q_ASSERT(!_aliasManager);
420     _aliasManager = new ClientAliasManager(this);
421     connect(aliasManager(), SIGNAL(initDone()), SLOT(sendBufferedUserInput()));
422     p->synchronize(aliasManager());
423
424     // create NetworkConfig
425     Q_ASSERT(!_networkConfig);
426     _networkConfig = new NetworkConfig("GlobalNetworkConfig", this);
427     p->synchronize(networkConfig());
428
429     // create IgnoreListManager
430     Q_ASSERT(!_ignoreListManager);
431     _ignoreListManager = new ClientIgnoreListManager(this);
432     p->synchronize(ignoreListManager());
433
434     // create Core-Side HighlightRuleManager
435     Q_ASSERT(!_highlightRuleManager);
436     _highlightRuleManager = new HighlightRuleManager(this);
437     p->synchronize(highlightRuleManager());
438
439 /*  not ready yet
440     // create TransferManager and DccConfig if core supports them
441     Q_ASSERT(!_dccConfig);
442     Q_ASSERT(!_transferManager);
443     if (isCoreFeatureEnabled(Quassel::Feature::DccFileTransfer)) {
444         _dccConfig = new DccConfig(this);
445         p->synchronize(dccConfig());
446         _transferManager = new ClientTransferManager(this);
447         _transferModel->setManager(_transferManager);
448         p->synchronize(transferManager());
449     }
450 */
451
452     // trigger backlog request once all active bufferviews are initialized
453     connect(bufferViewOverlay(), SIGNAL(initDone()), this, SLOT(finishConnectionInitialization()));
454
455     _connected = true;
456     emit connected();
457     emit coreConnectionStateChanged(true);
458 }
459
460 void Client::finishConnectionInitialization()
461 {
462     // usually it _should_ take longer until the bufferViews are initialized, so that's what
463     // triggers this slot. But we have to make sure that we know all buffers yet.
464     // so we check the BufferSyncer and in case it wasn't initialized we wait for that instead
465     if (!bufferSyncer()->isInitialized()) {
466         disconnect(bufferViewOverlay(), SIGNAL(initDone()), this, SLOT(finishConnectionInitialization()));
467         connect(bufferSyncer(), SIGNAL(initDone()), this, SLOT(finishConnectionInitialization()));
468         return;
469     }
470     disconnect(bufferViewOverlay(), SIGNAL(initDone()), this, SLOT(finishConnectionInitialization()));
471     disconnect(bufferSyncer(), SIGNAL(initDone()), this, SLOT(finishConnectionInitialization()));
472
473     requestInitialBacklog();
474     if (isCoreFeatureEnabled(Quassel::Feature::BufferActivitySync)) {
475         bufferSyncer()->markActivitiesChanged();
476         bufferSyncer()->markHighlightCountsChanged();
477     }
478 }
479
480
481 void Client::requestInitialBacklog()
482 {
483     _backlogManager->requestInitialBacklog();
484 }
485
486
487 void Client::requestLegacyCoreInfo()
488 {
489     // On older cores, the CoreInfo object was only synchronized on demand.  Synchronize now if
490     // needed.
491     if (isConnected() && !isCoreFeatureEnabled(Quassel::Feature::SyncedCoreInfo)) {
492         // Delete the existing core info object (it will always exist as client is single-threaded)
493         _coreInfo->deleteLater();
494         // No need to set to null when creating new one immediately after
495
496         // Create a fresh, unsynchronized CoreInfo object, emulating legacy behavior of CoreInfo not
497         // persisting
498         _coreInfo = new CoreInfo(this);
499         // Synchronize the new object
500         signalProxy()->synchronize(_coreInfo);
501
502         // Let others know signal handlers have been reset
503         emit coreInfoResynchronized();
504     }
505 }
506
507
508 void Client::disconnectFromCore()
509 {
510     if (!coreConnection()->isConnected())
511         return;
512
513     coreConnection()->disconnectFromCore();
514 }
515
516
517 void Client::setDisconnectedFromCore()
518 {
519     _connected = false;
520
521     emit disconnected();
522     emit coreConnectionStateChanged(false);
523
524     backlogManager()->reset();
525     messageProcessor()->reset();
526
527     // Clear internal data. Hopefully nothing relies on it at this point.
528
529     if (_bufferSyncer) {
530         _bufferSyncer->deleteLater();
531         _bufferSyncer = 0;
532     }
533
534     _coreInfo->reset();
535
536     if (_bufferViewManager) {
537         _bufferViewManager->deleteLater();
538         _bufferViewManager = 0;
539     }
540
541     _bufferViewOverlay->reset();
542
543     if (_aliasManager) {
544         _aliasManager->deleteLater();
545         _aliasManager = 0;
546     }
547
548     if (_ignoreListManager) {
549         _ignoreListManager->deleteLater();
550         _ignoreListManager = 0;
551     }
552
553     if (_highlightRuleManager) {
554         _highlightRuleManager->deleteLater();
555         _highlightRuleManager = nullptr;
556     }
557
558     if (_transferManager) {
559         _transferModel->setManager(nullptr);
560         _transferManager->deleteLater();
561         _transferManager = nullptr;
562     }
563
564     if (_dccConfig) {
565         _dccConfig->deleteLater();
566         _dccConfig = nullptr;
567     }
568
569     // we probably don't want to save pending input for reconnect
570     _userInputBuffer.clear();
571
572     _messageModel->clear();
573     _networkModel->clear();
574
575     QHash<NetworkId, Network *>::iterator netIter = _networks.begin();
576     while (netIter != _networks.end()) {
577         Network *net = netIter.value();
578         emit networkRemoved(net->networkId());
579         disconnect(net, SIGNAL(destroyed()), this, 0);
580         netIter = _networks.erase(netIter);
581         net->deleteLater();
582     }
583     Q_ASSERT(_networks.isEmpty());
584
585     QHash<IdentityId, Identity *>::iterator idIter = _identities.begin();
586     while (idIter != _identities.end()) {
587         emit identityRemoved(idIter.key());
588         Identity *id = idIter.value();
589         idIter = _identities.erase(idIter);
590         id->deleteLater();
591     }
592     Q_ASSERT(_identities.isEmpty());
593
594     if (_networkConfig) {
595         _networkConfig->deleteLater();
596         _networkConfig = 0;
597     }
598 }
599
600
601 /*** ***/
602
603 void Client::networkDestroyed()
604 {
605     Network *net = static_cast<Network *>(sender());
606     QHash<NetworkId, Network *>::iterator netIter = _networks.begin();
607     while (netIter != _networks.end()) {
608         if (*netIter == net) {
609             netIter = _networks.erase(netIter);
610             break;
611         }
612         else {
613             ++netIter;
614         }
615     }
616 }
617
618
619 // Hmm... we never used this...
620 void Client::recvStatusMsg(QString /*net*/, QString /*msg*/)
621 {
622     //recvMessage(net, Message::server("", QString("[STATUS] %1").arg(msg)));
623 }
624
625
626 void Client::recvMessage(const Message &msg)
627 {
628     Message msg_ = msg;
629     messageProcessor()->process(msg_);
630 }
631
632
633 void Client::setBufferLastSeenMsg(BufferId id, const MsgId &msgId)
634 {
635     if (bufferSyncer())
636         bufferSyncer()->requestSetLastSeenMsg(id, msgId);
637 }
638
639
640 void Client::setMarkerLine(BufferId id, const MsgId &msgId)
641 {
642     if (bufferSyncer())
643         bufferSyncer()->requestSetMarkerLine(id, msgId);
644 }
645
646
647 MsgId Client::markerLine(BufferId id)
648 {
649     if (id.isValid() && networkModel())
650         return networkModel()->markerLineMsgId(id);
651     return MsgId();
652 }
653
654
655 void Client::removeBuffer(BufferId id)
656 {
657     if (!bufferSyncer()) return;
658     bufferSyncer()->requestRemoveBuffer(id);
659 }
660
661
662 void Client::renameBuffer(BufferId bufferId, const QString &newName)
663 {
664     if (!bufferSyncer())
665         return;
666     bufferSyncer()->requestRenameBuffer(bufferId, newName);
667 }
668
669
670 void Client::mergeBuffersPermanently(BufferId bufferId1, BufferId bufferId2)
671 {
672     if (!bufferSyncer())
673         return;
674     bufferSyncer()->requestMergeBuffersPermanently(bufferId1, bufferId2);
675 }
676
677
678 void Client::purgeKnownBufferIds()
679 {
680     if (!bufferSyncer())
681         return;
682     bufferSyncer()->requestPurgeBufferIds();
683 }
684
685
686 void Client::bufferRemoved(BufferId bufferId)
687 {
688     // select a sane buffer (status buffer)
689     /* we have to manually select a buffer because otherwise inconsitent changes
690      * to the model might occur:
691      * the result of a buffer removal triggers a change in the selection model.
692      * the newly selected buffer might be a channel that hasn't been selected yet
693      * and a new nickview would be created (which never heard of the "rowsAboutToBeRemoved").
694      * this new view (and/or) its sort filter will then only receive a "rowsRemoved" signal.
695      */
696     QModelIndex current = bufferModel()->currentIndex();
697     if (current.data(NetworkModel::BufferIdRole).value<BufferId>() == bufferId) {
698         bufferModel()->setCurrentIndex(current.sibling(0, 0));
699     }
700
701     // and remove it from the model
702     networkModel()->removeBuffer(bufferId);
703 }
704
705
706 void Client::bufferRenamed(BufferId bufferId, const QString &newName)
707 {
708     QModelIndex bufferIndex = networkModel()->bufferIndex(bufferId);
709     if (bufferIndex.isValid()) {
710         networkModel()->setData(bufferIndex, newName, Qt::DisplayRole);
711     }
712 }
713
714
715 void Client::buffersPermanentlyMerged(BufferId bufferId1, BufferId bufferId2)
716 {
717     QModelIndex idx = networkModel()->bufferIndex(bufferId1);
718     bufferModel()->setCurrentIndex(bufferModel()->mapFromSource(idx));
719     networkModel()->removeBuffer(bufferId2);
720 }
721
722
723 void Client::markBufferAsRead(BufferId id)
724 {
725     if (bufferSyncer() && id.isValid())
726         bufferSyncer()->requestMarkBufferAsRead(id);
727 }
728
729
730 void Client::refreshLegacyCoreInfo()
731 {
732     instance()->requestLegacyCoreInfo();
733 }
734
735
736 void Client::changePassword(const QString &oldPassword, const QString &newPassword) {
737     CoreAccount account = currentCoreAccount();
738     account.setPassword(newPassword);
739     coreAccountModel()->createOrUpdateAccount(account);
740     emit instance()->requestPasswordChange(nullptr, account.user(), oldPassword, newPassword);
741 }
742
743
744 void Client::kickClient(int peerId)
745 {
746     emit instance()->requestKickClient(peerId);
747 }
748
749
750 void Client::corePasswordChanged(PeerPtr, bool success)
751 {
752     if (success)
753         coreAccountModel()->save();
754     emit passwordChanged(success);
755 }
756
757
758 #if QT_VERSION < 0x050000
759 void Client::logMessage(QtMsgType type, const char *msg)
760 {
761     fprintf(stderr, "%s\n", msg);
762     fflush(stderr);
763     if (type == QtFatalMsg) {
764         Quassel::logFatalMessage(msg);
765     }
766     else {
767         QString msgString = QString("%1\n").arg(msg);
768
769         //Check to see if there is an instance around, else we risk recursions
770         //when calling instance() and creating new ones.
771         if (!instanceExists())
772             return;
773
774         instance()->_debugLog << msgString;
775         emit instance()->logUpdated(msgString);
776     }
777 }
778 #else
779 void Client::logMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
780 {
781     Q_UNUSED(context);
782
783     fprintf(stderr, "%s\n", msg.toLocal8Bit().constData());
784     fflush(stderr);
785     if (type == QtFatalMsg) {
786         Quassel::logFatalMessage(msg.toLocal8Bit().constData());
787     }
788     else {
789         QString msgString = QString("%1\n").arg(msg);
790
791         //Check to see if there is an instance around, else we risk recursions
792         //when calling instance() and creating new ones.
793         if (!instanceExists())
794             return;
795
796         instance()->_debugLog << msgString;
797         emit instance()->logUpdated(msgString);
798     }
799 }
800 #endif