1 /***************************************************************************
2 * Copyright (C) 2005-2016 by the Quassel Project *
3 * devel@quassel-irc.org *
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. *
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. *
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 ***************************************************************************/
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"
45 #include "messagemodel.h"
47 #include "networkconfig.h"
48 #include "networkmodel.h"
50 #include "signalproxy.h"
51 #include "transfermodel.h"
53 #include "clientauthhandler.h"
58 QPointer<Client> Client::instanceptr = 0;
59 Quassel::Features Client::_coreFeatures = 0;
61 /*** Initialization/destruction ***/
63 bool Client::instanceExists()
69 Client *Client::instance()
72 instanceptr = new Client();
77 void Client::destroy()
80 delete instanceptr->mainUi();
81 instanceptr->deleteLater();
87 void Client::init(AbstractUi *ui)
89 instance()->_mainUi = ui;
94 Client::Client(QObject *parent)
96 _signalProxy(new SignalProxy(SignalProxy::Client, this)),
102 _backlogManager(new ClientBacklogManager(this)),
103 _bufferViewManager(0),
104 _bufferViewOverlay(new BufferViewOverlay(this)),
106 _ircListHelper(new ClientIrcListHelper(this)),
109 _ignoreListManager(0),
111 _transferModel(new TransferModel(this)),
113 _messageProcessor(0),
114 _coreAccountModel(new CoreAccountModel(this)),
115 _coreConnection(new CoreConnection(this)),
117 _debugLog(&_debugLogBuffer)
119 _signalProxy->synchronize(_ircListHelper);
125 disconnectFromCore();
131 _networkModel = new NetworkModel(this);
133 connect(this, SIGNAL(networkRemoved(NetworkId)),
134 _networkModel, SLOT(networkRemoved(NetworkId)));
136 _bufferModel = new BufferModel(_networkModel);
137 _messageModel = mainUi()->createMessageModel(this);
138 _messageProcessor = mainUi()->createMessageProcessor(this);
139 _inputHandler = new ClientUserInputHandler(this);
141 SignalProxy *p = signalProxy();
143 p->attachSlot(SIGNAL(displayMsg(const Message &)), this, SLOT(recvMessage(const Message &)));
144 p->attachSlot(SIGNAL(displayStatusMsg(QString, QString)), this, SLOT(recvStatusMsg(QString, QString)));
146 p->attachSlot(SIGNAL(bufferInfoUpdated(BufferInfo)), _networkModel, SLOT(bufferUpdated(BufferInfo)));
147 p->attachSignal(inputHandler(), SIGNAL(sendInput(BufferInfo, QString)));
148 p->attachSignal(this, SIGNAL(requestNetworkStates()));
150 p->attachSignal(this, SIGNAL(requestCreateIdentity(const Identity &, const QVariantMap &)), SIGNAL(createIdentity(const Identity &, const QVariantMap &)));
151 p->attachSignal(this, SIGNAL(requestRemoveIdentity(IdentityId)), SIGNAL(removeIdentity(IdentityId)));
152 p->attachSlot(SIGNAL(identityCreated(const Identity &)), this, SLOT(coreIdentityCreated(const Identity &)));
153 p->attachSlot(SIGNAL(identityRemoved(IdentityId)), this, SLOT(coreIdentityRemoved(IdentityId)));
155 p->attachSignal(this, SIGNAL(requestCreateNetwork(const NetworkInfo &, const QStringList &)), SIGNAL(createNetwork(const NetworkInfo &, const QStringList &)));
156 p->attachSignal(this, SIGNAL(requestRemoveNetwork(NetworkId)), SIGNAL(removeNetwork(NetworkId)));
157 p->attachSlot(SIGNAL(networkCreated(NetworkId)), this, SLOT(coreNetworkCreated(NetworkId)));
158 p->attachSlot(SIGNAL(networkRemoved(NetworkId)), this, SLOT(coreNetworkRemoved(NetworkId)));
160 p->attachSignal(this, SIGNAL(requestPasswordChange(PeerPtr,QString,QString,QString)), SIGNAL(changePassword(PeerPtr,QString,QString,QString)));
161 p->attachSlot(SIGNAL(passwordChanged(PeerPtr,bool)), this, SLOT(corePasswordChanged(PeerPtr,bool)));
163 p->attachSignal(this, SIGNAL(requestKickClient(int)), SIGNAL(kickClient(int)));
165 //connect(mainUi(), SIGNAL(connectToCore(const QVariantMap &)), this, SLOT(connectToCore(const QVariantMap &)));
166 connect(mainUi(), SIGNAL(disconnectFromCore()), this, SLOT(disconnectFromCore()));
167 connect(this, SIGNAL(connected()), mainUi(), SLOT(connectedToCore()));
168 connect(this, SIGNAL(disconnected()), mainUi(), SLOT(disconnectedFromCore()));
170 // attach backlog manager
171 p->synchronize(backlogManager());
172 connect(backlogManager(), SIGNAL(messagesReceived(BufferId, int)), _messageModel, SLOT(messagesReceived(BufferId, int)));
174 coreAccountModel()->load();
176 connect(coreConnection(), SIGNAL(stateChanged(CoreConnection::ConnectionState)), SLOT(connectionStateChanged(CoreConnection::ConnectionState)));
177 coreConnection()->init();
181 /*** public static methods ***/
183 AbstractUi *Client::mainUi()
185 return instance()->_mainUi;
189 void Client::setCoreFeatures(Quassel::Features features)
191 _coreFeatures = features;
195 bool Client::isConnected()
197 return instance()->_connected;
201 bool Client::internalCore()
203 return currentCoreAccount().isInternal();
207 /*** Network handling ***/
209 QList<NetworkId> Client::networkIds()
211 return instance()->_networks.keys();
215 const Network *Client::network(NetworkId networkid)
217 if (instance()->_networks.contains(networkid)) return instance()->_networks[networkid];
222 void Client::createNetwork(const NetworkInfo &info, const QStringList &persistentChannels)
224 emit instance()->requestCreateNetwork(info, persistentChannels);
228 void Client::removeNetwork(NetworkId id)
230 emit instance()->requestRemoveNetwork(id);
234 void Client::updateNetwork(const NetworkInfo &info)
236 Network *netptr = instance()->_networks.value(info.networkId, 0);
238 qWarning() << "Update for unknown network requested:" << info;
241 netptr->requestSetNetworkInfo(info);
245 void Client::addNetwork(Network *net)
247 net->setProxy(signalProxy());
248 signalProxy()->synchronize(net);
249 networkModel()->attachNetwork(net);
250 connect(net, SIGNAL(destroyed()), instance(), SLOT(networkDestroyed()));
251 instance()->_networks[net->networkId()] = net;
252 emit instance()->networkCreated(net->networkId());
256 void Client::coreNetworkCreated(NetworkId id)
258 if (_networks.contains(id)) {
259 qWarning() << "Creation of already existing network requested!";
262 Network *net = new Network(id, this);
267 void Client::coreNetworkRemoved(NetworkId id)
269 if (!_networks.contains(id))
271 Network *net = _networks.take(id);
272 emit networkRemoved(net->networkId());
277 /*** Identity handling ***/
279 QList<IdentityId> Client::identityIds()
281 return instance()->_identities.keys();
285 const Identity *Client::identity(IdentityId id)
287 if (instance()->_identities.contains(id)) return instance()->_identities[id];
292 void Client::createIdentity(const CertIdentity &id)
294 QVariantMap additional;
296 additional["KeyPem"] = id.sslKey().toPem();
297 additional["CertPem"] = id.sslCert().toPem();
299 emit instance()->requestCreateIdentity(id, additional);
303 void Client::updateIdentity(IdentityId id, const QVariantMap &ser)
305 Identity *idptr = instance()->_identities.value(id, 0);
307 qWarning() << "Update for unknown identity requested:" << id;
310 idptr->requestUpdate(ser);
314 void Client::removeIdentity(IdentityId id)
316 emit instance()->requestRemoveIdentity(id);
320 void Client::coreIdentityCreated(const Identity &other)
322 if (!_identities.contains(other.id())) {
323 Identity *identity = new Identity(other, this);
324 _identities[other.id()] = identity;
325 identity->setInitialized();
326 signalProxy()->synchronize(identity);
327 emit identityCreated(other.id());
330 qWarning() << tr("Identity already exists in client!");
335 void Client::coreIdentityRemoved(IdentityId id)
337 if (_identities.contains(id)) {
338 emit identityRemoved(id);
339 Identity *i = _identities.take(id);
345 /*** User input handling ***/
347 void Client::userInput(const BufferInfo &bufferInfo, const QString &message)
349 // we need to make sure that AliasManager is ready before processing input
350 if (aliasManager() && aliasManager()->isInitialized())
351 inputHandler()->handleUserInput(bufferInfo, message);
353 instance()->_userInputBuffer.append(qMakePair(bufferInfo, message));
357 void Client::sendBufferedUserInput()
359 for (int i = 0; i < _userInputBuffer.count(); i++)
360 userInput(_userInputBuffer.at(i).first, _userInputBuffer.at(i).second);
362 _userInputBuffer.clear();
366 /*** core connection stuff ***/
368 void Client::connectionStateChanged(CoreConnection::ConnectionState state)
371 case CoreConnection::Disconnected:
372 setDisconnectedFromCore();
374 case CoreConnection::Synchronized:
383 void Client::setSyncedToCore()
385 // create buffersyncer
386 Q_ASSERT(!_bufferSyncer);
387 _bufferSyncer = new BufferSyncer(this);
388 connect(bufferSyncer(), SIGNAL(lastSeenMsgSet(BufferId, MsgId)), _networkModel, SLOT(setLastSeenMsgId(BufferId, MsgId)));
389 connect(bufferSyncer(), SIGNAL(markerLineSet(BufferId, MsgId)), _networkModel, SLOT(setMarkerLineMsgId(BufferId, MsgId)));
390 connect(bufferSyncer(), SIGNAL(bufferRemoved(BufferId)), this, SLOT(bufferRemoved(BufferId)));
391 connect(bufferSyncer(), SIGNAL(bufferRenamed(BufferId, QString)), this, SLOT(bufferRenamed(BufferId, QString)));
392 connect(bufferSyncer(), SIGNAL(buffersPermanentlyMerged(BufferId, BufferId)), this, SLOT(buffersPermanentlyMerged(BufferId, BufferId)));
393 connect(bufferSyncer(), SIGNAL(buffersPermanentlyMerged(BufferId, BufferId)), _messageModel, SLOT(buffersPermanentlyMerged(BufferId, BufferId)));
394 connect(bufferSyncer(), SIGNAL(bufferMarkedAsRead(BufferId)), SIGNAL(bufferMarkedAsRead(BufferId)));
395 connect(bufferSyncer(), SIGNAL(bufferActivityChanged(BufferId, const Message::Types)), _networkModel, SLOT(bufferActivityChanged(BufferId, const Message::Types)));
396 connect(networkModel(), SIGNAL(requestSetLastSeenMsg(BufferId, MsgId)), bufferSyncer(), SLOT(requestSetLastSeenMsg(BufferId, const MsgId &)));
398 SignalProxy *p = signalProxy();
399 p->synchronize(bufferSyncer());
401 // create a new BufferViewManager
402 Q_ASSERT(!_bufferViewManager);
403 _bufferViewManager = new ClientBufferViewManager(p, this);
404 connect(_bufferViewManager, SIGNAL(initDone()), _bufferViewOverlay, SLOT(restore()));
406 // create AliasManager
407 Q_ASSERT(!_aliasManager);
408 _aliasManager = new ClientAliasManager(this);
409 connect(aliasManager(), SIGNAL(initDone()), SLOT(sendBufferedUserInput()));
410 p->synchronize(aliasManager());
412 // create NetworkConfig
413 Q_ASSERT(!_networkConfig);
414 _networkConfig = new NetworkConfig("GlobalNetworkConfig", this);
415 p->synchronize(networkConfig());
417 // create IgnoreListManager
418 Q_ASSERT(!_ignoreListManager);
419 _ignoreListManager = new ClientIgnoreListManager(this);
420 p->synchronize(ignoreListManager());
422 // create TransferManager and DccConfig if core supports them
423 Q_ASSERT(!_dccConfig);
424 Q_ASSERT(!_transferManager);
425 if (coreFeatures() & Quassel::DccFileTransfer) {
426 _dccConfig = new DccConfig(this);
427 p->synchronize(dccConfig());
428 _transferManager = new ClientTransferManager(this);
429 _transferModel->setManager(_transferManager);
430 p->synchronize(transferManager());
433 // trigger backlog request once all active bufferviews are initialized
434 connect(bufferViewOverlay(), SIGNAL(initDone()), this, SLOT(finishConnectionInitialization()));
438 emit coreConnectionStateChanged(true);
441 void Client::finishConnectionInitialization()
443 // usually it _should_ take longer until the bufferViews are initialized, so that's what
444 // triggers this slot. But we have to make sure that we know all buffers yet.
445 // so we check the BufferSyncer and in case it wasn't initialized we wait for that instead
446 if (!bufferSyncer()->isInitialized()) {
447 disconnect(bufferViewOverlay(), SIGNAL(initDone()), this, SLOT(finishConnectionInitialization()));
448 connect(bufferSyncer(), SIGNAL(initDone()), this, SLOT(finishConnectionInitialization()));
451 disconnect(bufferViewOverlay(), SIGNAL(initDone()), this, SLOT(finishConnectionInitialization()));
452 disconnect(bufferSyncer(), SIGNAL(initDone()), this, SLOT(finishConnectionInitialization()));
454 requestInitialBacklog();
455 if (coreFeatures().testFlag(Quassel::BufferActivitySync))
456 bufferSyncer()->markActivitiesChanged();
460 void Client::requestInitialBacklog()
462 _backlogManager->requestInitialBacklog();
466 void Client::disconnectFromCore()
468 if (!coreConnection()->isConnected())
471 coreConnection()->disconnectFromCore();
475 void Client::setDisconnectedFromCore()
481 emit coreConnectionStateChanged(false);
483 backlogManager()->reset();
484 messageProcessor()->reset();
486 // Clear internal data. Hopefully nothing relies on it at this point.
489 _bufferSyncer->deleteLater();
493 if (_bufferViewManager) {
494 _bufferViewManager->deleteLater();
495 _bufferViewManager = 0;
498 _bufferViewOverlay->reset();
501 _aliasManager->deleteLater();
505 if (_ignoreListManager) {
506 _ignoreListManager->deleteLater();
507 _ignoreListManager = 0;
510 if (_transferManager) {
511 _transferModel->setManager(nullptr);
512 _transferManager->deleteLater();
513 _transferManager = nullptr;
517 _dccConfig->deleteLater();
518 _dccConfig = nullptr;
521 // we probably don't want to save pending input for reconnect
522 _userInputBuffer.clear();
524 _messageModel->clear();
525 _networkModel->clear();
527 QHash<NetworkId, Network *>::iterator netIter = _networks.begin();
528 while (netIter != _networks.end()) {
529 Network *net = netIter.value();
530 emit networkRemoved(net->networkId());
531 disconnect(net, SIGNAL(destroyed()), this, 0);
532 netIter = _networks.erase(netIter);
535 Q_ASSERT(_networks.isEmpty());
537 QHash<IdentityId, Identity *>::iterator idIter = _identities.begin();
538 while (idIter != _identities.end()) {
539 emit identityRemoved(idIter.key());
540 Identity *id = idIter.value();
541 idIter = _identities.erase(idIter);
544 Q_ASSERT(_identities.isEmpty());
546 if (_networkConfig) {
547 _networkConfig->deleteLater();
555 void Client::networkDestroyed()
557 Network *net = static_cast<Network *>(sender());
558 QHash<NetworkId, Network *>::iterator netIter = _networks.begin();
559 while (netIter != _networks.end()) {
560 if (*netIter == net) {
561 netIter = _networks.erase(netIter);
571 // Hmm... we never used this...
572 void Client::recvStatusMsg(QString /*net*/, QString /*msg*/)
574 //recvMessage(net, Message::server("", QString("[STATUS] %1").arg(msg)));
578 void Client::recvMessage(const Message &msg)
581 messageProcessor()->process(msg_);
585 void Client::setBufferLastSeenMsg(BufferId id, const MsgId &msgId)
588 bufferSyncer()->requestSetLastSeenMsg(id, msgId);
592 void Client::setMarkerLine(BufferId id, const MsgId &msgId)
595 bufferSyncer()->requestSetMarkerLine(id, msgId);
599 MsgId Client::markerLine(BufferId id)
601 if (id.isValid() && networkModel())
602 return networkModel()->markerLineMsgId(id);
607 void Client::removeBuffer(BufferId id)
609 if (!bufferSyncer()) return;
610 bufferSyncer()->requestRemoveBuffer(id);
614 void Client::renameBuffer(BufferId bufferId, const QString &newName)
618 bufferSyncer()->requestRenameBuffer(bufferId, newName);
622 void Client::mergeBuffersPermanently(BufferId bufferId1, BufferId bufferId2)
626 bufferSyncer()->requestMergeBuffersPermanently(bufferId1, bufferId2);
630 void Client::purgeKnownBufferIds()
634 bufferSyncer()->requestPurgeBufferIds();
638 void Client::bufferRemoved(BufferId bufferId)
640 // select a sane buffer (status buffer)
641 /* we have to manually select a buffer because otherwise inconsitent changes
642 * to the model might occur:
643 * the result of a buffer removal triggers a change in the selection model.
644 * the newly selected buffer might be a channel that hasn't been selected yet
645 * and a new nickview would be created (which never heard of the "rowsAboutToBeRemoved").
646 * this new view (and/or) its sort filter will then only receive a "rowsRemoved" signal.
648 QModelIndex current = bufferModel()->currentIndex();
649 if (current.data(NetworkModel::BufferIdRole).value<BufferId>() == bufferId) {
650 bufferModel()->setCurrentIndex(current.sibling(0, 0));
653 // and remove it from the model
654 networkModel()->removeBuffer(bufferId);
658 void Client::bufferRenamed(BufferId bufferId, const QString &newName)
660 QModelIndex bufferIndex = networkModel()->bufferIndex(bufferId);
661 if (bufferIndex.isValid()) {
662 networkModel()->setData(bufferIndex, newName, Qt::DisplayRole);
667 void Client::buffersPermanentlyMerged(BufferId bufferId1, BufferId bufferId2)
669 QModelIndex idx = networkModel()->bufferIndex(bufferId1);
670 bufferModel()->setCurrentIndex(bufferModel()->mapFromSource(idx));
671 networkModel()->removeBuffer(bufferId2);
675 void Client::markBufferAsRead(BufferId id)
677 if (bufferSyncer() && id.isValid())
678 bufferSyncer()->requestMarkBufferAsRead(id);
682 void Client::changePassword(const QString &oldPassword, const QString &newPassword) {
683 CoreAccount account = currentCoreAccount();
684 account.setPassword(newPassword);
685 coreAccountModel()->createOrUpdateAccount(account);
686 emit instance()->requestPasswordChange(nullptr, account.user(), oldPassword, newPassword);
690 void Client::kickClient(int peerId) {
691 emit instance()->requestKickClient(peerId);
695 void Client::corePasswordChanged(PeerPtr, bool success)
698 coreAccountModel()->save();
699 emit passwordChanged(success);
703 #if QT_VERSION < 0x050000
704 void Client::logMessage(QtMsgType type, const char *msg)
706 fprintf(stderr, "%s\n", msg);
708 if (type == QtFatalMsg) {
709 Quassel::logFatalMessage(msg);
712 QString msgString = QString("%1\n").arg(msg);
714 //Check to see if there is an instance around, else we risk recursions
715 //when calling instance() and creating new ones.
716 if (!instanceExists())
719 instance()->_debugLog << msgString;
720 emit instance()->logUpdated(msgString);
724 void Client::logMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
728 fprintf(stderr, "%s\n", msg.toLocal8Bit().constData());
730 if (type == QtFatalMsg) {
731 Quassel::logFatalMessage(msg.toLocal8Bit().constData());
734 QString msgString = QString("%1\n").arg(msg);
736 //Check to see if there is an instance around, else we risk recursions
737 //when calling instance() and creating new ones.
738 if (!instanceExists())
741 instance()->_debugLog << msgString;
742 emit instance()->logUpdated(msgString);