+ IrcUser *ircuser = e->network()->updateNickFromMask(e->prefix());
+ if (ircuser) {
+ // WHOX uses '0' to indicate logged-out, account-notify and extended-join uses '*'.
+ // As '*' is used internally to represent logged-out, no need to handle that differently.
+ ircuser->setAccount(e->params().at(0));
+ } else {
+ qDebug() << "Received account-notify data for unknown user" << e->prefix();
+ }
+}
+
+/* IRCv3 away-notify - ":nick!user@host AWAY [:message]" */
+void CoreSessionEventProcessor::processIrcEventAway(IrcEvent *e)
+{
+ if (!checkParamCount(e, 1))
+ return;
+ // Don't use checkParamCount(e, 2) since the message is optional. Some servers respond in a way
+ // that it counts as two parameters, but we shouldn't rely on that.
+
+ // 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 two parameters are sent -and- the second parameter isn't empty, then user is away.
+ // Otherwise, mark them as not away.
+ if (e->params().count() >= 2 && !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);
+ }
+}
+
+/* IRCv3 chghost - ":nick!user@host CHGHOST newuser new.host.goes.here" */
+void CoreSessionEventProcessor::processIrcEventChghost(IrcEvent *e)
+{
+ if (!checkParamCount(e, 2))
+ return;
+
+ IrcUser *ircuser = e->network()->updateNickFromMask(e->prefix());
+ if (ircuser) {
+ // Update with new user/hostname information. setUser/setHost handles checking what
+ // actually changed.
+ ircuser->setUser(e->params().at(0));
+ ircuser->setHost(e->params().at(1));
+ } else {
+ qDebug() << "Received chghost data for unknown user" << e->prefix();
+ }
+}
+
+void CoreSessionEventProcessor::processIrcEventInvite(IrcEvent *e)
+{
+ if (checkParamCount(e, 2)) {
+ e->network()->updateNickFromMask(e->prefix());
+ }
+}
+
+/* JOIN: ":<nick!user@host> JOIN <channel>" */
+void CoreSessionEventProcessor::processIrcEventJoin(IrcEvent *e)
+{
+ if (e->testFlag(EventManager::Fake)) // generated by handleEarlyNetsplitJoin
+ return;
+
+ if (!checkParamCount(e, 1))
+ return;
+
+ CoreNetwork *net = coreNetwork(e);
+ QString channel = e->params()[0];
+ IrcUser *ircuser = net->updateNickFromMask(e->prefix());
+
+ if (net->capEnabled(IrcCap::EXTENDED_JOIN)) {
+ if (e->params().count() < 3) {
+ // Some IRC servers don't send extended-join events in all situations. Rather than
+ // ignore the join entirely, treat it as a regular join with a debug-level log entry.
+ // See: https://github.com/inspircd/inspircd/issues/821
+ qDebug() << "extended-join requires 3 params, got:" << e->params() << ", handling as a "
+ "regular join";
+ } else {
+ // If logged in, :nick!user@host JOIN #channelname accountname :Real Name
+ // If logged out, :nick!user@host JOIN #channelname * :Real Name
+ // See: http://ircv3.net/specs/extensions/extended-join-3.1.html
+ // WHOX uses '0' to indicate logged-out, account-notify and extended-join uses '*'.
+ // As '*' is used internally to represent logged-out, no need to handle that differently.
+ ircuser->setAccount(e->params()[1]);
+ // Update the user's real name, too
+ ircuser->setRealName(e->params()[2]);
+ }
+ }
+ // Else :nick!user@host JOIN #channelname
+
+ bool handledByNetsplit = false;
+ foreach(Netsplit* n, _netsplits.value(e->network())) {
+ handledByNetsplit = n->userJoined(e->prefix(), channel);
+ if (handledByNetsplit)
+ 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 (net->capEnabled(IrcCap::AWAY_NOTIFY)) {
+ net->queueAutoWhoOneshot(ircuser->nick());
+ }
+
+ if (!handledByNetsplit)
+ ircuser->joinChannel(channel);
+ else
+ e->setFlag(EventManager::Netsplit);
+
+ if (net->isMe(ircuser)) {
+ net->setChannelJoined(channel);
+ // Mark the message as Self
+ e->setFlag(EventManager::Self);
+ // FIXME use event
+ net->putRawLine(net->serverEncode("MODE " + channel)); // we want to know the modes of the channel we just joined, so we ask politely
+ }
+}
+
+
+void CoreSessionEventProcessor::lateProcessIrcEventKick(IrcEvent *e)
+{
+ if (checkParamCount(e, 2)) {
+ e->network()->updateNickFromMask(e->prefix());
+ IrcUser *victim = e->network()->ircUser(e->params().at(1));
+ if (victim) {
+ victim->partChannel(e->params().at(0));
+ //if(e->network()->isMe(victim)) e->network()->setKickedFromChannel(channel);
+ }
+ }
+}
+
+
+void CoreSessionEventProcessor::processIrcEventMode(IrcEvent *e)
+{
+ if (!checkParamCount(e, 2))
+ return;
+
+ if (e->network()->isChannelName(e->params().first())) {
+ // Channel Modes
+
+ IrcChannel *channel = e->network()->ircChannel(e->params()[0]);
+ if (!channel) {
+ // we received mode information for a channel we're not in. that means probably we've just been kicked out or something like that
+ // anyways: we don't have a place to store the data --> discard the info.
+ return;
+ }
+
+ QString modes = e->params()[1];
+ bool add = true;
+ int paramOffset = 2;
+ for (int c = 0; c < modes.length(); c++) {
+ if (modes[c] == '+') {
+ add = true;
+ continue;
+ }
+ if (modes[c] == '-') {
+ add = false;
+ continue;
+ }
+
+ if (e->network()->prefixModes().contains(modes[c])) {
+ // user channel modes (op, voice, etc...)
+ if (paramOffset < e->params().count()) {
+ IrcUser *ircUser = e->network()->ircUser(e->params()[paramOffset]);
+ if (!ircUser) {
+ qWarning() << Q_FUNC_INFO << "Unknown IrcUser:" << e->params()[paramOffset];
+ }
+ else {
+ if (add) {
+ bool handledByNetsplit = false;
+ QHash<QString, Netsplit *> splits = _netsplits.value(e->network());
+ foreach(Netsplit* n, _netsplits.value(e->network())) {
+ handledByNetsplit = n->userAlreadyJoined(ircUser->hostmask(), channel->name());
+ if (handledByNetsplit) {
+ n->addMode(ircUser->hostmask(), channel->name(), QString(modes[c]));
+ break;
+ }
+ }
+ if (!handledByNetsplit)
+ channel->addUserMode(ircUser, QString(modes[c]));
+ }
+ else
+ channel->removeUserMode(ircUser, QString(modes[c]));
+ }
+ }
+ else {
+ qWarning() << "Received MODE with too few parameters:" << e->params();
+ }
+ ++paramOffset;
+ }
+ else {
+ // regular channel modes
+ QString value;
+ Network::ChannelModeType modeType = e->network()->channelModeType(modes[c]);
+ if (modeType == Network::A_CHANMODE || modeType == Network::B_CHANMODE || (modeType == Network::C_CHANMODE && add)) {
+ if (paramOffset < e->params().count()) {
+ value = e->params()[paramOffset];
+ }
+ else {
+ qWarning() << "Received MODE with too few parameters:" << e->params();
+ }
+ ++paramOffset;
+ }
+
+ if (add)
+ channel->addChannelMode(modes[c], value);
+ else
+ channel->removeChannelMode(modes[c], value);
+ }
+ }
+ }
+ else {
+ // pure User Modes
+ IrcUser *ircUser = e->network()->newIrcUser(e->params().first());
+ QString modeString(e->params()[1]);
+ QString addModes;
+ QString removeModes;
+ bool add = false;
+ for (int c = 0; c < modeString.count(); c++) {
+ if (modeString[c] == '+') {
+ add = true;
+ continue;
+ }
+ if (modeString[c] == '-') {
+ add = false;
+ continue;
+ }
+ if (add)
+ addModes += modeString[c];
+ else
+ removeModes += modeString[c];
+ }
+ if (!addModes.isEmpty())
+ ircUser->addUserModes(addModes);
+ if (!removeModes.isEmpty())
+ ircUser->removeUserModes(removeModes);
+
+ if (e->network()->isMe(ircUser)) {
+ // Mark the message as Self
+ e->setFlag(EventManager::Self);
+ coreNetwork(e)->updatePersistentModes(addModes, removeModes);
+ }
+ }
+}
+
+
+void CoreSessionEventProcessor::processIrcEventNick(IrcEvent *e)
+{
+ if (checkParamCount(e, 1)) {
+ IrcUser *ircuser = e->network()->updateNickFromMask(e->prefix());
+ if (!ircuser) {
+ qWarning() << Q_FUNC_INFO << "Unknown IrcUser!";
+ return;
+ }