void CoreNetwork::setChannelJoined(const QString &channel)
{
- _autoWhoQueue.prepend(channel.toLower()); // prepend so this new chan is the first to be checked
+ queueAutoWhoOneshot(channel); // check this new channel first
Core::setChannelPersistent(userId(), networkId(), channel, true);
Core::setPersistentChannelKey(userId(), networkId(), channel, _channelKeys[channel.toLower()]);
// Handle special cases here
// TODO Use events if it makes sense
+ if (capability == "away-notify") {
+ // away-notify enabled, stop the automatic timers, handle manually
+ setAutoWhoEnabled(false);
+ }
}
void CoreNetwork::removeCap(const QString &capability)
// Handle special cases here
// TODO Use events if it makes sense
+ if (capability == "away-notify") {
+ // away-notify disabled, enable autowho according to configuration
+ setAutoWhoEnabled(networkConfig()->autoWhoEnabled());
+ }
}
QString CoreNetwork::capValue(const QString &capability) const
_autoWhoQueue = channels();
}
+void CoreNetwork::queueAutoWhoOneshot(const QString &channelOrNick)
+{
+ // Prepend so these new channels/nicks are the first to be checked
+ // Don't allow duplicates
+ if (!_autoWhoQueue.contains(channelOrNick.toLower())) {
+ _autoWhoQueue.prepend(channelOrNick.toLower());
+ }
+ if (useCapAwayNotify()) {
+ // When away-notify is active, the timer's stopped. Start a new cycle to who this channel.
+ setAutoWhoEnabled(true);
+ }
+}
+
void CoreNetwork::setAutoWhoDelay(int delay)
{
return;
while (!_autoWhoQueue.isEmpty()) {
- QString chan = _autoWhoQueue.takeFirst();
- IrcChannel *ircchan = ircChannel(chan);
- if (!ircchan) continue;
- if (networkConfig()->autoWhoNickLimit() > 0 && ircchan->ircUsers().count() >= networkConfig()->autoWhoNickLimit())
+ QString chanOrNick = _autoWhoQueue.takeFirst();
+ // Check if it's a known channel or nick
+ IrcChannel *ircchan = ircChannel(chanOrNick);
+ IrcUser *ircuser = ircUser(chanOrNick);
+ if (ircchan) {
+ // Apply channel limiting rules
+ // If using away-notify, don't impose channel size limits in order to capture away
+ // state of everyone. Auto-who won't run on a timer so network impact is minimal.
+ if (networkConfig()->autoWhoNickLimit() > 0
+ && ircchan->ircUsers().count() >= networkConfig()->autoWhoNickLimit()
+ && !useCapAwayNotify())
+ continue;
+ _autoWhoPending[chanOrNick.toLower()]++;
+ } else if (ircuser) {
+ // Checking a nick, add it to the pending list
+ _autoWhoPending[ircuser->nick().toLower()]++;
+ } else {
+ // Not a channel or a nick, skip it
+ qDebug() << "Skipping who polling of unknown channel or nick" << chanOrNick;
continue;
- _autoWhoPending[chan]++;
- putRawLine("WHO " + serverEncode(chan));
+ }
+ // TODO Use WHO extended to poll away users and/or user accounts
+ // If a server supports it, supports("WHOX") will be true
+ // See: http://faerion.sourceforge.net/doc/irc/whox.var and HexChat
+ putRawLine("WHO " + serverEncode(chanOrNick));
break;
}
- if (_autoWhoQueue.isEmpty() && networkConfig()->autoWhoEnabled() && !_autoWhoCycleTimer.isActive()) {
+
+ if (_autoWhoQueue.isEmpty() && networkConfig()->autoWhoEnabled() && !_autoWhoCycleTimer.isActive()
+ && !useCapAwayNotify()) {
// Timer was stopped, means a new cycle is due immediately
+ // Don't run a new cycle if using away-notify; server will notify as appropriate
_autoWhoCycleTimer.start();
startAutoWhoCycle();
+ } else if (useCapAwayNotify() && _autoWhoCycleTimer.isActive()) {
+ // Don't run another who cycle if away-notify is enabled
+ _autoWhoCycleTimer.stop();
}
}
*/
inline bool useCapSASL() const { return capEnabled("sasl"); }
+ /**
+ * Gets the status of the away-notify capability.
+ *
+ * http://ircv3.net/specs/extensions/away-notify-3.1.html
+ *
+ * @returns True if away-notify is enabled, otherwise false
+ */
+ inline bool useCapAwayNotify() const { return capEnabled("away-notify"); }
+
public slots:
virtual void setMyNick(const QString &mynick);
void setAutoWhoInterval(int interval);
void setAutoWhoDelay(int delay);
+ /**
+ * Appends the given channel/nick to the front of the AutoWho queue.
+ *
+ * When 'away-notify' is enabled, this will trigger an immediate AutoWho since regular
+ * who-cycles are disabled as per IRCv3 specifications.
+ *
+ * @param[in] channelOrNick Channel or nickname to WHO
+ */
+ void queueAutoWhoOneshot(const QString &channelOrNick);
+
bool setAutoWhoDone(const QString &channel);
void updateIssuedModes(const QString &requestedModes);
// Only request SASL if it's enabled
if (coreNet->networkInfo().useSasl)
queueCurrentCap = true;
+ } else if (availableCapPair.at(0).startsWith("away-notify")) {
+ // Always request these capabilities if available
+ queueCurrentCap = true;
}
if (queueCurrentCap) {
if(availableCapPair.count() >= 2)
}
+/* IRCv3 away-notify - ":nick!user@host AWAY [:message]" */
+void CoreSessionEventProcessor::processIrcEventAway(IrcEvent *e)
+{
+ if (!checkParamCount(e, 2))
+ return;
+
+ // Nick is sent as part of parameters in order to split user/server decoding
+ IrcUser *ircuser = e->network()->ircUser(e->params().at(0));
+ if (ircuser) {
+ if (!e->params().at(1).isEmpty()) {
+ ircuser->setAway(true);
+ ircuser->setAwayMessage(e->params().at(1));
+ } else {
+ ircuser->setAway(false);
+ }
+ } else {
+ qDebug() << "Received away-notify data for unknown user" << e->params().at(0);
+ }
+}
+
void CoreSessionEventProcessor::processIrcEventInvite(IrcEvent *e)
{
if (checkParamCount(e, 2)) {
break;
}
+ // If using away-notify, check new users. Works around buggy IRC servers
+ // forgetting to send :away messages for users who join channels when away.
+ if (coreNetwork(e)->useCapAwayNotify()) {
+ coreNetwork(e)->queueAutoWhoOneshot(ircuser->nick());
+ }
+
if (!handledByNetsplit)
ircuser->joinChannel(channel);
else
ircuser->setRealName(e->params().last().section(" ", 1));
}
- if (coreNetwork(e)->isAutoWhoInProgress(channel))
+ // Check if channel name has a who in progress.
+ // If not, then check if user nick exists and has a who in progress.
+ if (coreNetwork(e)->isAutoWhoInProgress(channel) ||
+ (ircuser && coreNetwork(e)->isAutoWhoInProgress(ircuser->nick()))) {
e->setFlag(EventManager::Silent);
+ }
}