X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcommon%2Fircuser.cpp;h=ec1ecb1c93c903c93b3386fbf30a4c9da29b3b92;hp=64f5d3234db2d5dcf103150b8b8306afb4294e20;hb=7298446b3e86140c4bbbfe7aeeb959c16c53363c;hpb=4aca5963302eb2f6dfc41b723df4b362704f1b2b diff --git a/src/common/ircuser.cpp b/src/common/ircuser.cpp index 64f5d323..ec1ecb1c 100644 --- a/src/common/ircuser.cpp +++ b/src/common/ircuser.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2005-08 by the Quassel Project * + * Copyright (C) 2005-2020 by the Quassel Project * * devel@quassel-irc.org * * * * This program is free software; you can redistribute it and/or modify * @@ -15,300 +15,389 @@ * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "ircuser.h" -#include "util.h" +#include +#include + +#include "ircchannel.h" #include "network.h" #include "signalproxy.h" -#include "ircchannel.h" - -#include -#include +#include "util.h" -IrcUser::IrcUser(const QString &hostmask, Network *network) - : SyncableObject(network), - _initialized(false), - _nick(nickFromMask(hostmask)), - _user(userFromMask(hostmask)), - _host(hostFromMask(hostmask)), - _realName(), - _awayMessage(), - _away(false), - _server(), - _idleTime(QDateTime::currentDateTime()), - _ircOperator(), - _lastAwayMessage(0), - _network(network), - _codecForEncoding(0), - _codecForDecoding(0) +IrcUser::IrcUser(const QString& hostmask, Network* network) + : SyncableObject(network) + , _initialized(false) + , _nick(nickFromMask(hostmask)) + , _user(userFromMask(hostmask)) + , _host(hostFromMask(hostmask)) + , _realName() + , _awayMessage() + , _away(false) + , _server() + , + // _idleTime(QDateTime::currentDateTime()), + _ircOperator() + , _lastAwayMessageTime() + , _whoisServiceReply() + , _encrypted(false) + , _network(network) + , _codecForEncoding(nullptr) + , _codecForDecoding(nullptr) { - updateObjectName(); -} - -IrcUser::~IrcUser() { + updateObjectName(); + _lastAwayMessageTime.setTimeSpec(Qt::UTC); + _lastAwayMessageTime.setMSecsSinceEpoch(0); } // ==================== // PUBLIC: // ==================== -QString IrcUser::user() const { - return _user; -} - -QString IrcUser::host() const { - return _host; -} - -QString IrcUser::nick() const { - return _nick; +QString IrcUser::hostmask() const +{ + return QString("%1!%2@%3").arg(nick()).arg(user()).arg(host()); } -QString IrcUser::realName() const { - return _realName; +QDateTime IrcUser::idleTime() +{ + if ((QDateTime::currentDateTime().toMSecsSinceEpoch() - _idleTimeSet.toMSecsSinceEpoch()) > 1200000) { + // 20 * 60 * 1000 = 1200000 + // 20 minutes have elapsed, clear the known idle time as it's likely inaccurate by now + _idleTime = QDateTime(); + } + return _idleTime; } -QString IrcUser::hostmask() const { - return QString("%1!%2@%3").arg(nick()).arg(user()).arg(host()); +QStringList IrcUser::channels() const +{ + QStringList chanList; + IrcChannel* channel; + foreach (channel, _channels) { + chanList << channel->name(); + } + return chanList; } -bool IrcUser::isAway() const { - return _away; +void IrcUser::setCodecForEncoding(const QString& name) +{ + setCodecForEncoding(QTextCodec::codecForName(name.toLatin1())); } -QString IrcUser::awayMessage() const { - return _awayMessage; +void IrcUser::setCodecForEncoding(QTextCodec* codec) +{ + _codecForEncoding = codec; } -QString IrcUser::server() const { - return _server; +void IrcUser::setCodecForDecoding(const QString& name) +{ + setCodecForDecoding(QTextCodec::codecForName(name.toLatin1())); } -QDateTime IrcUser::idleTime() const { - return _idleTime; +void IrcUser::setCodecForDecoding(QTextCodec* codec) +{ + _codecForDecoding = codec; } -QString IrcUser::ircOperator() const { - return _ircOperator; +QString IrcUser::decodeString(const QByteArray& text) const +{ + if (!codecForDecoding()) + return network()->decodeString(text); + return ::decodeString(text, codecForDecoding()); } -int IrcUser::lastAwayMessage() const { - return _lastAwayMessage; +QByteArray IrcUser::encodeString(const QString& string) const +{ + if (codecForEncoding()) { + return codecForEncoding()->fromUnicode(string); + } + return network()->encodeString(string); } -QString IrcUser::userModes() const { - return _userModes; +// ==================== +// PUBLIC SLOTS: +// ==================== +void IrcUser::setUser(const QString& user) +{ + if (!user.isEmpty() && _user != user) { + _user = user; + SYNC(ARG(user)); + } } -QStringList IrcUser::channels() const { - QStringList chanList; - IrcChannel *channel; - foreach(channel, _channels) { - chanList << channel->name(); - } - return chanList; +void IrcUser::setRealName(const QString& realName) +{ + if (!realName.isEmpty() && _realName != realName) { + _realName = realName; + SYNC(ARG(realName)) + } } -Network* IrcUser::network() const { - return _network; +void IrcUser::setAccount(const QString& account) +{ + if (_account != account) { + _account = account; + SYNC(ARG(account)) + } } -QTextCodec *IrcUser::codecForEncoding() const { - return _codecForEncoding; +void IrcUser::setAway(bool away) +{ + if (away != _away) { + _away = away; + markAwayChanged(); + SYNC(ARG(away)) + emit awaySet(away); + } } -void IrcUser::setCodecForEncoding(const QString &name) { - setCodecForEncoding(QTextCodec::codecForName(name.toAscii())); +void IrcUser::setAwayMessage(const QString& awayMessage) +{ + if (!awayMessage.isEmpty() && _awayMessage != awayMessage) { + _awayMessage = awayMessage; + markAwayChanged(); + SYNC(ARG(awayMessage)) + } } -void IrcUser::setCodecForEncoding(QTextCodec *codec) { - _codecForEncoding = codec; +void IrcUser::setIdleTime(const QDateTime& idleTime) +{ + if (idleTime.isValid() && _idleTime != idleTime) { + _idleTime = idleTime; + _idleTimeSet = QDateTime::currentDateTime(); + SYNC(ARG(idleTime)) + } } -QTextCodec *IrcUser::codecForDecoding() const { - return _codecForDecoding; +void IrcUser::setLoginTime(const QDateTime& loginTime) +{ + if (loginTime.isValid() && _loginTime != loginTime) { + _loginTime = loginTime; + SYNC(ARG(loginTime)) + } } -void IrcUser::setCodecForDecoding(const QString &name) { - setCodecForDecoding(QTextCodec::codecForName(name.toAscii())); +void IrcUser::setServer(const QString& server) +{ + if (!server.isEmpty() && _server != server) { + _server = server; + SYNC(ARG(server)) + } } -void IrcUser::setCodecForDecoding(QTextCodec *codec) { - _codecForDecoding = codec; +void IrcUser::setIrcOperator(const QString& ircOperator) +{ + if (!ircOperator.isEmpty() && _ircOperator != ircOperator) { + _ircOperator = ircOperator; + SYNC(ARG(ircOperator)) + } } -QString IrcUser::decodeString(const QByteArray &text) const { - if(!codecForDecoding()) return network()->decodeString(text); - return ::decodeString(text, codecForDecoding()); +// This function is only ever called by SYNC calls from legacy cores (pre-0.13). +// Therefore, no SYNC call is needed here. +void IrcUser::setLastAwayMessage(int lastAwayMessage) +{ +#if QT_VERSION >= 0x050800 + QDateTime lastAwayMessageTime = QDateTime::fromSecsSinceEpoch(lastAwayMessage); +#else + // toSecsSinceEpoch() was added in Qt 5.8. Manually downconvert to seconds for now. + // See https://doc.qt.io/qt-5/qdatetime.html#toMSecsSinceEpoch + QDateTime lastAwayMessageTime = QDateTime::fromMSecsSinceEpoch(lastAwayMessage * 1000); +#endif + lastAwayMessageTime.setTimeSpec(Qt::UTC); + setLastAwayMessageTime(lastAwayMessageTime); +} + +void IrcUser::setLastAwayMessageTime(const QDateTime& lastAwayMessageTime) +{ + if (lastAwayMessageTime > _lastAwayMessageTime) { + _lastAwayMessageTime = lastAwayMessageTime; + SYNC(ARG(lastAwayMessageTime)) + } } -QByteArray IrcUser::encodeString(const QString string) const { - if(codecForEncoding()) { - return codecForEncoding()->fromUnicode(string); - } - return network()->encodeString(string); +void IrcUser::setHost(const QString& host) +{ + if (!host.isEmpty() && _host != host) { + _host = host; + SYNC(ARG(host)) + } } -// ==================== -// PUBLIC SLOTS: -// ==================== -void IrcUser::setUser(const QString &user) { - if(!user.isEmpty() && _user != user) { - _user = user; - emit userSet(user); - } +void IrcUser::setNick(const QString& nick) +{ + if (!nick.isEmpty() && nick != _nick) { + _nick = nick; + updateObjectName(); + SYNC(ARG(nick)) + emit nickSet(nick); + } } -void IrcUser::setRealName(const QString &realName) { - if (!realName.isEmpty() && _realName != realName) { - _realName = realName; - emit realNameSet(realName); - } +void IrcUser::setWhoisServiceReply(const QString& whoisServiceReply) +{ + if (!whoisServiceReply.isEmpty() && whoisServiceReply != _whoisServiceReply) { + _whoisServiceReply = whoisServiceReply; + SYNC(ARG(whoisServiceReply)) + } } -void IrcUser::setAway(const bool &away) { - if(away != _away) { - _away = away; - emit awaySet(away); - } +void IrcUser::setSuserHost(const QString& suserHost) +{ + if (!suserHost.isEmpty() && suserHost != _suserHost) { + _suserHost = suserHost; + SYNC(ARG(suserHost)) + } } -void IrcUser::setAwayMessage(const QString &awayMessage) { - if(!awayMessage.isEmpty() && _awayMessage != awayMessage) { - _awayMessage = awayMessage; - emit awayMessageSet(awayMessage); - } +void IrcUser::setEncrypted(bool encrypted) +{ + _encrypted = encrypted; + emit encryptedSet(encrypted); + SYNC(ARG(encrypted)) } -void IrcUser::setIdleTime(const QDateTime &idleTime) { - if(idleTime.isValid() && _idleTime != idleTime) { - _idleTime = idleTime; - emit idleTimeSet(idleTime); - } +void IrcUser::updateObjectName() +{ + setObjectName(QString::number(network()->networkId().toInt()) + "/" + _nick); } -void IrcUser::setServer(const QString &server) { - if(!server.isEmpty() && _server != server) { - _server = server; - emit serverSet(server); - } -} +void IrcUser::updateHostmask(const QString& mask) +{ + if (mask == hostmask()) + return; -void IrcUser::setIrcOperator(const QString &ircOperator) { - if(!ircOperator.isEmpty() && _ircOperator != ircOperator) { - _ircOperator = ircOperator; - emit ircOperatorSet(ircOperator); - } + QString user = userFromMask(mask); + QString host = hostFromMask(mask); + setUser(user); + setHost(host); } -void IrcUser::setLastAwayMessage(const int &lastAwayMessage) { - if(lastAwayMessage > _lastAwayMessage) { - _lastAwayMessage = lastAwayMessage; - emit lastAwayMessageSet(lastAwayMessage); - } +void IrcUser::joinChannel(IrcChannel* channel, bool skip_channel_join) +{ + Q_ASSERT(channel); + if (!_channels.contains(channel)) { + _channels.insert(channel); + if (!skip_channel_join) + channel->joinIrcUser(this); + } } -void IrcUser::setHost(const QString &host) { - if(!host.isEmpty() && _host != host) { - _host = host; - emit hostSet(host); - } +void IrcUser::joinChannel(const QString& channelname) +{ + joinChannel(network()->newIrcChannel(channelname)); } -void IrcUser::setNick(const QString &nick) { - if(!nick.isEmpty() && nick != _nick) { - _nick = nick; - updateObjectName(); - emit nickSet(nick); - } +void IrcUser::partChannel(IrcChannel* channel) +{ + partChannelInternal(channel, false); } -void IrcUser::updateObjectName() { - QString newName = QString::number(network()->networkId().toInt()) + "/" + _nick; - QString oldName = objectName(); - if(oldName != newName) { - setObjectName(newName); - emit renameObject(oldName, newName); - } +void IrcUser::partChannel(const QString& channelname) +{ + IrcChannel* channel = network()->ircChannel(channelname); + if (channel == nullptr) { + qWarning() << "IrcUser::partChannel(): received part for unknown Channel" << channelname; + } + else { + partChannel(channel); + } } -void IrcUser::updateHostmask(const QString &mask) { - if(mask == hostmask()) - return; - - QString user = userFromMask(mask); - QString host = hostFromMask(mask); - setUser(user); - setHost(host); +void IrcUser::partChannelInternal(IrcChannel* channel, bool skip_sync) +{ + if (_channels.contains(channel)) { + _channels.remove(channel); + disconnect(channel, nullptr, this, nullptr); + channel->part(this); + QString channelName = channel->name(); + if (!skip_sync) SYNC_OTHER(partChannel, ARG(channelName)) + if (_channels.isEmpty() && !network()->isMe(this)) + quitInternal(skip_sync); + } +} + +void IrcUser::quit() +{ + quitInternal(false); } -void IrcUser::joinChannel(IrcChannel *channel) { - Q_ASSERT(channel); - if(!_channels.contains(channel)) { - _channels.insert(channel); - channel->join(this); - connect(channel, SIGNAL(destroyed()), this, SLOT(channelDestroyed())); - emit channelJoined(channel->name()); - } +void IrcUser::quitInternal(bool skip_sync) +{ + QList channels = _channels.values(); + _channels.clear(); + foreach (IrcChannel* channel, channels) { + disconnect(channel, nullptr, this, nullptr); + channel->part(this); + } + network()->removeIrcUser(this); + if (!skip_sync) SYNC(NO_ARG) + emit quited(); +} + +void IrcUser::channelDestroyed() +{ + // private slot! + auto* channel = static_cast(sender()); + if (_channels.contains(channel)) { + _channels.remove(channel); + if (_channels.isEmpty() && !network()->isMe(this)) + quit(); + } } -void IrcUser::joinChannel(const QString &channelname) { - joinChannel(network()->newIrcChannel(channelname)); +void IrcUser::setUserModes(const QString& modes) +{ + if (_userModes != modes) { + _userModes = modes; + SYNC(ARG(modes)) + emit userModesSet(modes); + } } -void IrcUser::partChannel(IrcChannel *channel) { - if(_channels.contains(channel)) { - _channels.remove(channel); - disconnect(channel, 0, this, 0); - channel->part(this); - emit channelParted(channel->name()); - } -} +void IrcUser::addUserModes(const QString& modes) +{ + if (modes.isEmpty()) + return; -void IrcUser::partChannel(const QString &channelname) { - IrcChannel *channel = network()->ircChannel(channelname); - if(channel == 0) { - qWarning() << "IrcUser::partChannel(): received part for unknown Channel" << channelname; - } else { - partChannel(channel); - } -} + // Don't needlessly sync when no changes are made + bool changesMade = false; + for (int i = 0; i < modes.count(); i++) { + if (!_userModes.contains(modes[i])) { + _userModes += modes[i]; + changesMade = true; + } + } -void IrcUser::channelDestroyed() { - // private slot! - IrcChannel *channel = static_cast(sender()); - Q_ASSERT(channel); - if(_channels.contains(channel)) { - _channels.remove(channel); - disconnect(channel, 0, this, 0); - } + if (changesMade) { + SYNC(ARG(modes)) + emit userModesAdded(modes); + } } -void IrcUser::setUserModes(const QString &modes) { - _userModes = modes; - emit userModesSet(modes); -} +void IrcUser::removeUserModes(const QString& modes) +{ + if (modes.isEmpty()) + return; -void IrcUser::addUserMode(const QString &mode) { - if(!_userModes.contains(mode)) { - _userModes += mode; - emit userModeAdded(mode); - } + for (int i = 0; i < modes.count(); i++) { + _userModes.remove(modes[i]); + } + SYNC(ARG(modes)) + emit userModesRemoved(modes); } -void IrcUser::removeUserMode(const QString &mode) { - if(_userModes.contains(mode)) { - _userModes.remove(mode); - emit userModeRemoved(mode); - } +void IrcUser::setLastChannelActivity(BufferId buffer, const QDateTime& time) +{ + _lastActivity[buffer] = time; + emit lastChannelActivityUpdated(buffer, time); } -void IrcUser::initSetChannels(const QStringList channels) { - foreach(QString channel, channels) { - joinChannel(channel); - } +void IrcUser::setLastSpokenTo(BufferId buffer, const QDateTime& time) +{ + _lastSpokenTo[buffer] = time; + emit lastSpokenToUpdated(buffer, time); } -