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