/***************************************************************************
- * Copyright (C) 2005-2015 by the Quassel Project *
+ * Copyright (C) 2005-2018 by the Quassel Project *
* devel@quassel-irc.org *
* *
* This program is free software; you can redistribute it and/or modify *
#include "corebuffersyncer.h"
#include "corebacklogmanager.h"
#include "corebufferviewmanager.h"
+#include "coredccconfig.h"
#include "coreeventmanager.h"
#include "coreidentity.h"
#include "coreignorelistmanager.h"
+#include "coreinfo.h"
#include "coreirclisthelper.h"
#include "corenetwork.h"
#include "corenetworkconfig.h"
#include "ircchannel.h"
#include "ircparser.h"
#include "ircuser.h"
-#include "logger.h"
+#include "logmessage.h"
#include "messageevent.h"
#include "remotepeer.h"
#include "storage.h"
};
-CoreSession::CoreSession(UserId uid, bool restoreState, QObject *parent)
+CoreSession::CoreSession(UserId uid, bool restoreState, bool strictIdentEnabled, QObject *parent)
: QObject(parent),
_user(uid),
+ _strictIdentEnabled(strictIdentEnabled),
_signalProxy(new SignalProxy(SignalProxy::Server, this)),
_aliasManager(this),
_bufferSyncer(new CoreBufferSyncer(this)),
_backlogManager(new CoreBacklogManager(this)),
_bufferViewManager(new CoreBufferViewManager(_signalProxy, this)),
+ _dccConfig(new CoreDccConfig(this)),
_ircListHelper(new CoreIrcListHelper(this)),
_networkConfig(new CoreNetworkConfig("GlobalNetworkConfig", this)),
- _coreInfo(this),
+ _coreInfo(new CoreInfo(this)),
_transferManager(new CoreTransferManager(this)),
_eventManager(new CoreEventManager(this)),
_eventStringifier(new EventStringifier(this)),
_ircParser(new IrcParser(this)),
scriptEngine(new QScriptEngine(this)),
_processMessages(false),
- _ignoreListManager(this)
+ _ignoreListManager(this),
+ _highlightRuleManager(this)
{
SignalProxy *p = signalProxy();
p->setHeartBeatInterval(30);
p->attachSlot(SIGNAL(changePassword(PeerPtr,QString,QString,QString)), this, SLOT(changePassword(PeerPtr,QString,QString,QString)));
p->attachSignal(this, SIGNAL(passwordChanged(PeerPtr,bool)));
+ p->attachSlot(SIGNAL(kickClient(int)), this, SLOT(kickClient(int)));
+ p->attachSignal(this, SIGNAL(disconnectFromCore()));
+
+ QVariantMap data;
+ data["quasselVersion"] = Quassel::buildInfo().fancyVersionString;
+ data["quasselBuildDate"] = Quassel::buildInfo().commitDate; // "BuildDate" for compatibility
+ data["startTime"] = Core::instance()->startTime();
+ data["sessionConnectedClients"] = 0;
+ _coreInfo->setCoreData(data);
+
loadSettings();
initScriptEngine();
eventManager()->registerObject(ctcpParser(), EventManager::LowPriority, "send");
// periodically save our session state
- connect(&(Core::instance()->syncTimer()), SIGNAL(timeout()), this, SLOT(saveSessionState()));
+ connect(Core::instance()->syncTimer(), SIGNAL(timeout()), this, SLOT(saveSessionState()));
p->synchronize(_bufferSyncer);
p->synchronize(&aliasManager());
p->synchronize(_backlogManager);
+ p->synchronize(dccConfig());
p->synchronize(ircListHelper());
p->synchronize(networkConfig());
- p->synchronize(&_coreInfo);
+ p->synchronize(_coreInfo);
p->synchronize(&_ignoreListManager);
+ p->synchronize(&_highlightRuleManager);
+ // Listen to network removed events
+ connect(this, SIGNAL(networkRemoved(NetworkId)),
+ &_highlightRuleManager, SLOT(networkRemoved(NetworkId)));
p->synchronize(transferManager());
// Restore session state
if (restoreState)
}
-CoreSession::~CoreSession()
+void CoreSession::shutdown()
{
saveSessionState();
- foreach(CoreNetwork *net, _networks.values()) {
- delete net;
+
+ // Request disconnect from all connected networks in parallel, and wait until every network
+ // has emitted the disconnected() signal before deleting the session itself
+ for (CoreNetwork *net : _networks.values()) {
+ if (net->socketState() != QAbstractSocket::UnconnectedState) {
+ _networksPendingDisconnect.insert(net->networkId());
+ connect(net, SIGNAL(disconnected(NetworkId)), this, SLOT(onNetworkDisconnected(NetworkId)));
+ net->shutdown();
+ }
+ }
+
+ if (_networksPendingDisconnect.isEmpty()) {
+ // Nothing to do, suicide so the core can shut down
+ deleteLater();
+ }
+}
+
+
+void CoreSession::onNetworkDisconnected(NetworkId networkId)
+{
+ _networksPendingDisconnect.remove(networkId);
+ if (_networksPendingDisconnect.isEmpty()) {
+ // We're done, suicide so the core can shut down
+ deleteLater();
}
}
void CoreSession::addClient(RemotePeer *peer)
{
+ signalProxy()->setTargetPeer(peer);
+
peer->dispatch(sessionState());
signalProxy()->addPeer(peer);
+ _coreInfo->setConnectedClientData(signalProxy()->peerCount(), signalProxy()->peerData());
+
+ signalProxy()->setTargetPeer(nullptr);
}
RemotePeer *p = qobject_cast<RemotePeer *>(peer);
if (p)
quInfo() << qPrintable(tr("Client")) << p->description() << qPrintable(tr("disconnected (UserId: %1).").arg(user().toInt()));
+ _coreInfo->setConnectedClientData(signalProxy()->peerCount(), signalProxy()->peerData());
}
}
+QHash<QString, QByteArray> CoreSession::bufferCiphers(NetworkId id) const
+{
+ return Core::bufferCiphers(user(), id);
+}
+
+void CoreSession::setBufferCipher(NetworkId id, const QString &bufferName, const QByteArray &cipher) const
+{
+ Core::setBufferCipher(user(), id, bufferName, cipher);
+}
+
+
// FIXME switch to BufferId
void CoreSession::msgFromClient(BufferInfo bufinfo, QString msg)
{
if (_ignoreListManager.match(rawMsg, networkName) == IgnoreListManager::HardStrictness)
return;
+
+ if (currentNetwork && _highlightRuleManager.match(rawMsg, currentNetwork->myNick(), currentNetwork->identityPtr()->nicks()))
+ rawMsg.flags |= Message::Flag::Highlight;
+
_messageQueue << rawMsg;
if (!_processMessages) {
_processMessages = true;
Q_ASSERT(!createBuffer);
bufferInfo = Core::bufferInfo(user(), rawMsg.networkId, BufferInfo::StatusBuffer, "");
}
- Message msg(bufferInfo, rawMsg.type, rawMsg.text, rawMsg.sender, rawMsg.flags);
+ Message msg(bufferInfo, rawMsg.type, rawMsg.text, rawMsg.sender, senderPrefixes(rawMsg.sender, bufferInfo),
+ realName(rawMsg.sender, rawMsg.networkId), avatarUrl(rawMsg.sender, rawMsg.networkId),
+ rawMsg.flags);
if(Core::storeMessage(msg))
emit displayMsg(msg);
}
}
bufferInfoCache[rawMsg.networkId][rawMsg.target] = bufferInfo;
}
- Message msg(bufferInfo, rawMsg.type, rawMsg.text, rawMsg.sender, rawMsg.flags);
+ Message msg(bufferInfo, rawMsg.type, rawMsg.text, rawMsg.sender, senderPrefixes(rawMsg.sender, bufferInfo),
+ realName(rawMsg.sender, rawMsg.networkId), avatarUrl(rawMsg.sender, rawMsg.networkId),
+ rawMsg.flags);
messages << msg;
}
// add the StatusBuffer to the Cache in case there are more Messages for the original target
bufferInfoCache[rawMsg.networkId][rawMsg.target] = bufferInfo;
}
- Message msg(bufferInfo, rawMsg.type, rawMsg.text, rawMsg.sender, rawMsg.flags);
+ Message msg(bufferInfo, rawMsg.type, rawMsg.text, rawMsg.sender, senderPrefixes(rawMsg.sender, bufferInfo),
+ realName(rawMsg.sender, rawMsg.networkId), avatarUrl(rawMsg.sender, rawMsg.networkId),
+ rawMsg.flags);
messages << msg;
}
_messageQueue.clear();
}
+QString CoreSession::senderPrefixes(const QString &sender, const BufferInfo &bufferInfo) const
+{
+ CoreNetwork *currentNetwork = network(bufferInfo.networkId());
+ if (!currentNetwork) {
+ return {};
+ }
+
+ if (bufferInfo.type() != BufferInfo::ChannelBuffer) {
+ return {};
+ }
+
+ IrcChannel *currentChannel = currentNetwork->ircChannel(bufferInfo.bufferName());
+ if (!currentChannel) {
+ return {};
+ }
+
+ const QString modes = currentChannel->userModes(nickFromMask(sender).toLower());
+ return currentNetwork->modesToPrefixes(modes);
+}
+
+QString CoreSession::realName(const QString &sender, NetworkId networkId) const
+{
+ CoreNetwork *currentNetwork = network(networkId);
+ if (!currentNetwork) {
+ return {};
+ }
+
+ IrcUser *currentUser = currentNetwork->ircUser(nickFromMask(sender));
+ if (!currentUser) {
+ return {};
+ }
+
+ return currentUser->realName();
+}
+
+QString CoreSession::avatarUrl(const QString &sender, NetworkId networkId) const
+{
+ Q_UNUSED(sender);
+ Q_UNUSED(networkId);
+ // Currently we do not have a way to retrieve this value yet.
+ //
+ // This likely will require implementing IRCv3's METADATA spec.
+ // See https://ircv3.net/irc/
+ // And https://blog.irccloud.com/avatars/
+ return "";
+}
Protocol::SessionState CoreSession::sessionState() const
{
createIdentity(coreIdentity);
}
+const QString CoreSession::strictCompliantIdent(const CoreIdentity *identity) {
+ if (_strictIdentEnabled) {
+ // Strict mode enabled: only allow the user's Quassel username as an ident
+ return Core::instance()->strictSysIdent(_user);
+ } else {
+ // Strict mode disabled: allow any identity specified
+ return identity->ident();
+ }
+}
void CoreSession::createIdentity(const CoreIdentity &identity)
{
if (!identity->detachAwayReason().isEmpty())
awayReason = identity->detachAwayReason();
net->setAutoAwayActive(true);
+ // Allow handleAway() to format the current date/time in the string.
net->userInputHandler()->handleAway(BufferInfo(), awayReason);
}
}
}
-void CoreSession::globalAway(const QString &msg)
+void CoreSession::globalAway(const QString &msg, const bool skipFormatting)
{
QHash<NetworkId, CoreNetwork *>::iterator netIter = _networks.begin();
CoreNetwork *net = 0;
if (!net->isConnected())
continue;
- net->userInputHandler()->issueAway(msg, false /* no force away */);
+ net->userInputHandler()->issueAway(msg, false /* no force away */, skipFormatting);
}
}
-void CoreSession::changePassword(PeerPtr peer, const QString &userName, const QString &oldPassword, const QString &newPassword)
-{
+void CoreSession::changePassword(PeerPtr peer, const QString &userName, const QString &oldPassword, const QString &newPassword) {
+ Q_UNUSED(peer);
+
bool success = false;
UserId uid = Core::validateUser(userName, oldPassword);
if (uid.isValid() && uid == user())
success = Core::changeUserPassword(uid, newPassword);
- emit passwordChanged(peer, success);
+ signalProxy()->restrictTargetPeers(signalProxy()->sourcePeer(), [&]{
+ emit passwordChanged(nullptr, success);
+ });
+}
+
+void CoreSession::kickClient(int peerId) {
+ auto peer = signalProxy()->peerById(peerId);
+ if (peer == nullptr) {
+ qWarning() << "Invalid peer Id: " << peerId;
+ return;
+ }
+ signalProxy()->restrictTargetPeers(peer, [&]{
+ emit disconnectFromCore();
+ });
}