CmdResult Handle (const std::vector<std::string> ¶meters, User *user) { Channel* channel = ServerInstance->FindChan(parameters[0]); std::string reason = ConvToStr("Cycling"); if (parameters.size() > 1) { /* reason provided, use it */ reason = reason + ": " + parameters[1]; } if (!channel) { user->WriteNumeric(403, "%s %s :No such channel", user->nick.c_str(), parameters[0].c_str()); return CMD_FAILURE; } if (channel->HasUser(user)) { /* * technically, this is only ever sent locally, but pays to be safe ;p */ if (IS_LOCAL(user)) { if (channel->GetPrefixValue(user) < VOICE_VALUE && channel->IsBanned(user)) { /* banned, boned. drop the message. */ user->WriteServ("NOTICE "+user->nick+" :*** You may not cycle, as you are banned on channel " + channel->name); return CMD_FAILURE; } channel->PartUser(user, reason); Channel::JoinUser(user, parameters[0], true, "", false, ServerInstance->Time()); } return CMD_SUCCESS; } else { user->WriteNumeric(442, "%s %s :You're not on that channel", user->nick.c_str(), channel->name.c_str()); } return CMD_FAILURE; }
CmdResult CommandPrivmsg::Handle (const std::vector<std::string>& parameters, User *user) { User *dest; Channel *chan; CUList except_list; LocalUser* localuser = IS_LOCAL(user); if (localuser) localuser->idle_lastmsg = ServerInstance->Time(); if (ServerInstance->Parser->LoopCall(user, this, parameters, 0)) return CMD_SUCCESS; if (parameters[0][0] == '$') { if (!user->HasPrivPermission("users/mass-message")) return CMD_SUCCESS; ModResult MOD_RESULT; std::string temp = parameters[1]; FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, (void*)parameters[0].c_str(), TYPE_SERVER, temp, 0, except_list, MSG_PRIVMSG)); if (MOD_RESULT == MOD_RES_DENY) return CMD_FAILURE; const char* text = temp.c_str(); const char* servermask = (parameters[0].c_str()) + 1; FOREACH_MOD(I_OnText,OnText(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list)); if (InspIRCd::Match(ServerInstance->Config->ServerName, servermask, NULL)) { user->SendAll("PRIVMSG", "%s", text); } FOREACH_MOD(I_OnUserMessage,OnUserMessage(user, (void*)parameters[0].c_str(), TYPE_SERVER, text, 0, except_list, MSG_PRIVMSG)); return CMD_SUCCESS; } char status = 0; const char* target = parameters[0].c_str(); if (ServerInstance->Modes->FindPrefix(*target)) { status = *target; target++; } if (*target == '#') { chan = ServerInstance->FindChan(target); except_list.insert(user); if (chan) { if (localuser && chan->GetPrefixValue(user) < VOICE_VALUE) { if (chan->IsModeSet('n') && !chan->HasUser(user)) { user->WriteNumeric(404, "%s %s :Cannot send to channel (no external messages)", user->nick.c_str(), chan->name.c_str()); return CMD_FAILURE; } if (chan->IsModeSet('m')) { user->WriteNumeric(404, "%s %s :Cannot send to channel (+m)", user->nick.c_str(), chan->name.c_str()); return CMD_FAILURE; } if (ServerInstance->Config->RestrictBannedUsers) { if (chan->IsBanned(user)) { user->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", user->nick.c_str(), chan->name.c_str()); return CMD_FAILURE; } } } ModResult MOD_RESULT; std::string temp = parameters[1]; FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, chan, TYPE_CHANNEL, temp, status, except_list, MSG_PRIVMSG)); if (MOD_RESULT == MOD_RES_DENY) return CMD_FAILURE; const char* text = temp.c_str(); /* Check again, a module may have zapped the input string */ if (temp.empty()) { user->WriteNumeric(412, "%s :No text to send", user->nick.c_str()); return CMD_FAILURE; } FOREACH_MOD(I_OnText,OnText(user,chan,TYPE_CHANNEL,text,status,except_list)); if (status) { if (ServerInstance->Config->UndernetMsgPrefix) { chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%c %s", status, chan->name.c_str(), status, text); } else { chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %c%s :%s", status, chan->name.c_str(), text); } } else { chan->WriteAllExcept(user, false, status, except_list, "PRIVMSG %s :%s", chan->name.c_str(), text); } FOREACH_MOD(I_OnUserMessage, OnUserMessage(user,chan, TYPE_CHANNEL, text, status, except_list, MSG_PRIVMSG)); } else { /* no such nick/channel */ user->WriteNumeric(401, "%s %s :No such nick/channel", user->nick.c_str(), target); return CMD_FAILURE; } return CMD_SUCCESS; } const char* destnick = parameters[0].c_str(); if (localuser) { const char* targetserver = strchr(destnick, '@'); if (targetserver) { std::string nickonly; nickonly.assign(destnick, 0, targetserver - destnick); dest = ServerInstance->FindNickOnly(nickonly); if (dest && strcasecmp(dest->server.c_str(), targetserver + 1)) { /* Incorrect server for user */ user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str()); return CMD_FAILURE; } } else dest = ServerInstance->FindNickOnly(destnick); } else dest = ServerInstance->FindNick(destnick); if ((dest) && (dest->registered == REG_ALL)) { if (parameters[1].empty()) { user->WriteNumeric(412, "%s :No text to send", user->nick.c_str()); return CMD_FAILURE; } if (dest->IsAway()) { /* auto respond with aweh msg */ user->WriteNumeric(301, "%s %s :%s", user->nick.c_str(), dest->nick.c_str(), dest->awaymsg.c_str()); } ModResult MOD_RESULT; std::string temp = parameters[1]; FIRST_MOD_RESULT(OnUserPreMessage, MOD_RESULT, (user, dest, TYPE_USER, temp, 0, except_list, MSG_PRIVMSG)); if (MOD_RESULT == MOD_RES_DENY) return CMD_FAILURE; const char* text = temp.c_str(); FOREACH_MOD(I_OnText,OnText(user, dest, TYPE_USER, text, 0, except_list)); if (IS_LOCAL(dest)) { // direct write, same server user->WriteTo(dest, "PRIVMSG %s :%s", dest->nick.c_str(), text); } FOREACH_MOD(I_OnUserMessage,OnUserMessage(user, dest, TYPE_USER, text, 0, except_list, MSG_PRIVMSG)); } else { /* no such nick/channel */ user->WriteNumeric(401, "%s %s :No such nick/channel",user->nick.c_str(), parameters[0].c_str()); return CMD_FAILURE; } return CMD_SUCCESS; }
bool User::ChangeNick(const std::string& newnick, bool force, time_t newts) { if (quitting) { ServerInstance->Logs->Log("USERS", LOG_DEFAULT, "ERROR: Attempted to change nick of a quitting user: "******"%s :Cannot send to channel (you're banned)", chan->name.c_str()); return false; } } } } if (assign(newnick) == assign(nick)) { // case change, don't need to check campers // and, if it's identical including case, we can leave right now // We also don't update the nick TS if it's a case change, either if (newnick == nick) return true; } else { /* * Uh oh.. if the nickname is in use, and it's not in use by the person using it (doh) -- * then we have a potential collide. Check whether someone else is camping on the nick * (i.e. connect -> send NICK, don't send USER.) If they are camping, force-change the * camper to their UID, and allow the incoming nick change. * * If the guy using the nick is already using it, tell the incoming nick change to gtfo, * because the nick is already (rightfully) in use. -- w00t */ User* InUse = ServerInstance->FindNickOnly(newnick); if (InUse && (InUse != this)) { if (InUse->registered != REG_ALL) { /* force the camper to their UUID, and ask them to re-send a NICK. */ InUse->WriteFrom(InUse, "NICK %s", InUse->uuid.c_str()); InUse->WriteNumeric(ERR_NICKNAMEINUSE, "%s :Nickname overruled.", InUse->nick.c_str()); ServerInstance->Users->clientlist.erase(InUse->nick); ServerInstance->Users->clientlist[InUse->uuid] = InUse; InUse->nick = InUse->uuid; InUse->InvalidateCache(); InUse->registered &= ~REG_NICK; } else { /* No camping, tell the incoming user to stop trying to change nick ;p */ this->WriteNumeric(ERR_NICKNAMEINUSE, "%s :Nickname is already in use.", newnick.c_str()); return false; } } age = newts ? newts : ServerInstance->Time(); } if (this->registered == REG_ALL) this->WriteCommon("NICK %s",newnick.c_str()); std::string oldnick = nick; nick = newnick; InvalidateCache(); ServerInstance->Users->clientlist.erase(oldnick); ServerInstance->Users->clientlist[newnick] = this; if (registered == REG_ALL) FOREACH_MOD(OnUserPostNick, (this,oldnick)); return true; }
bool User::ChangeNick(const std::string& newnick, bool force) { ModResult MOD_RESULT; if (force) ServerInstance->NICKForced.set(this, 1); FIRST_MOD_RESULT(OnUserPreNick, MOD_RESULT, (this, newnick)); ServerInstance->NICKForced.set(this, 0); if (MOD_RESULT == MOD_RES_DENY) { ServerInstance->stats->statsCollisions++; return false; } if (assign(newnick) == assign(nick)) { // case change, don't need to check Q:lines and such // and, if it's identical including case, we can leave right now if (newnick == nick) return true; } else { /* * Don't check Q:Lines if it's a server-enforced change, just on the off-chance some f*****g *moron* * tries to Q:Line SIDs, also, this means we just get our way period, as it really should be. * Thanks Kein for finding this. -- w00t * * Also don't check Q:Lines for remote nickchanges, they should have our Q:Lines anyway to enforce themselves. * -- w00t */ if (IS_LOCAL(this) && !force) { XLine* mq = ServerInstance->XLines->MatchesLine("Q",newnick); if (mq) { if (this->registered == REG_ALL) { ServerInstance->SNO->WriteGlobalSno('a', "Q-Lined nickname %s from %s: %s", newnick.c_str(), GetFullRealHost().c_str(), mq->reason.c_str()); } this->WriteNumeric(432, "%s %s :Invalid nickname: %s",this->nick.c_str(), newnick.c_str(), mq->reason.c_str()); return false; } if (ServerInstance->Config->RestrictBannedUsers) { for (UCListIter i = this->chans.begin(); i != this->chans.end(); i++) { Channel *chan = *i; if (chan->GetPrefixValue(this) < VOICE_VALUE && chan->IsBanned(this)) { this->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", this->nick.c_str(), chan->name.c_str()); return false; } } } } /* * Uh oh.. if the nickname is in use, and it's not in use by the person using it (doh) -- * then we have a potential collide. Check whether someone else is camping on the nick * (i.e. connect -> send NICK, don't send USER.) If they are camping, force-change the * camper to their UID, and allow the incoming nick change. * * If the guy using the nick is already using it, tell the incoming nick change to gtfo, * because the nick is already (rightfully) in use. -- w00t */ User* InUse = ServerInstance->FindNickOnly(newnick); if (InUse && (InUse != this)) { if (InUse->registered != REG_ALL) { /* force the camper to their UUID, and ask them to re-send a NICK. */ InUse->WriteTo(InUse, "NICK %s", InUse->uuid.c_str()); InUse->WriteNumeric(433, "%s %s :Nickname overruled.", InUse->nick.c_str(), InUse->nick.c_str()); ServerInstance->Users->clientlist->erase(InUse->nick); (*(ServerInstance->Users->clientlist))[InUse->uuid] = InUse; InUse->nick = InUse->uuid; InUse->InvalidateCache(); InUse->registered &= ~REG_NICK; } else { /* No camping, tell the incoming user to stop trying to change nick ;p */ this->WriteNumeric(433, "%s %s :Nickname is already in use.", this->registered >= REG_NICK ? this->nick.c_str() : "*", newnick.c_str()); return false; } } } if (this->registered == REG_ALL) this->WriteCommon("NICK %s",newnick.c_str()); std::string oldnick = nick; nick = newnick; InvalidateCache(); ServerInstance->Users->clientlist->erase(oldnick); (*(ServerInstance->Users->clientlist))[newnick] = this; if (registered == REG_ALL) FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(this,oldnick)); return true; }
CmdResult HandleChannelTarget(User* source, const Params& parameters, const char* target, PrefixMode* pm) { Channel* chan = ServerInstance->FindChan(target); if (!chan) { // The target channel does not exist. source->WriteNumeric(Numerics::NoSuchChannel(parameters[0])); return CMD_FAILURE; } if (IS_LOCAL(source)) { if (chan->IsModeSet(noextmsgmode) && !chan->HasUser(source)) { // The noextmsg mode is set and the source is not in the channel. source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (no external messages)"); return CMD_FAILURE; } bool no_chan_priv = chan->GetPrefixValue(source) < VOICE_VALUE; if (no_chan_priv && chan->IsModeSet(moderatedmode)) { // The moderated mode is set and the source has no status rank. source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (+m)"); return CMD_FAILURE; } if (no_chan_priv && ServerInstance->Config->RestrictBannedUsers != ServerConfig::BUT_NORMAL && chan->IsBanned(source)) { // The source is banned in the channel and restrictbannedusers is enabled. if (ServerInstance->Config->RestrictBannedUsers == ServerConfig::BUT_RESTRICT_NOTIFY) source->WriteNumeric(ERR_CANNOTSENDTOCHAN, chan->name, "Cannot send to channel (you're banned)"); return CMD_FAILURE; } } // Fire the pre-message events. MessageTarget msgtarget(chan, pm ? pm->GetPrefix() : 0); CTCTags::TagMessageDetails msgdetails(parameters.GetTags()); if (!FirePreEvents(source, msgtarget, msgdetails)) return CMD_FAILURE; unsigned int minrank = pm ? pm->GetPrefixRank() : 0; CTCTags::TagMessage message(source, chan, parameters.GetTags()); const Channel::MemberMap& userlist = chan->GetUsers(); for (Channel::MemberMap::const_iterator iter = userlist.begin(); iter != userlist.end(); ++iter) { LocalUser* luser = IS_LOCAL(iter->first); // Don't send to remote users or the user who is the source. if (!luser || luser == source) continue; // Don't send to unprivileged or exempt users. if (iter->second->getRank() < minrank || msgdetails.exemptions.count(luser)) continue; // Send to users if they have the capability. if (cap.get(luser)) luser->Send(msgevprov, message); } return FirePostEvent(source, msgtarget, msgdetails); }
/** Handle nick changes from users. * NOTE: If you are used to ircds based on ircd2.8, and are looking * for the client introduction code in here, youre in the wrong place. * You need to look in the spanningtree module for this! */ CmdResult CommandNick::Handle (const std::vector<std::string>& parameters, User *user) { std::string oldnick; if (parameters[0].empty()) { /* We cant put blanks in the parameters, so for this (extremely rare) issue we just put '*' here. */ user->WriteNumeric(432, "%s * :Erroneous Nickname", user->nick.empty() ? user->nick.c_str() : "*"); return CMD_FAILURE; } if (((!ServerInstance->IsNick(parameters[0].c_str(), ServerInstance->Config->Limits.NickMax))) && (IS_LOCAL(user))) { if (!allowinvalid) { if (parameters[0] == "0") { // Special case, Fake a /nick UIDHERE. Useful for evading "ERR: NICK IN USE" on connect etc. std::vector<std::string> p2; std::deque<classbase*> dummy; p2.push_back(user->uuid); this->HandleInternal(1, dummy); this->Handle(p2, user); this->HandleInternal(0, dummy); return CMD_SUCCESS; } user->WriteNumeric(432, "%s %s :Erroneous Nickname", user->nick.c_str(),parameters[0].c_str()); return CMD_FAILURE; } } if (assign(user->nick) == parameters[0]) { /* If its exactly the same, even case, dont do anything. */ if (parameters[0] == user->nick) { return CMD_SUCCESS; } /* Its a change of case. People insisted that they should be * able to do silly things like this even though the RFC says * the nick AAA is the same as the nick aaa. */ oldnick.assign(user->nick, 0, IS_LOCAL(user) ? ServerInstance->Config->Limits.NickMax : MAXBUF); int MOD_RESULT = 0; FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(user,parameters[0])); if (MOD_RESULT) return CMD_FAILURE; if (user->registered == REG_ALL) user->WriteCommon("NICK %s",parameters[0].c_str()); user->nick.assign(parameters[0], 0, IS_LOCAL(user) ? ServerInstance->Config->Limits.NickMax : MAXBUF); user->InvalidateCache(); FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(user,oldnick)); return CMD_SUCCESS; } else { /* * Don't check Q:Lines if it's a server-enforced change, just on the off-chance some f*****g *moron* * tries to Q:Line SIDs, also, this means we just get our way period, as it really should be. * Thanks Kein for finding this. -- w00t * * Also don't check Q:Lines for remote nickchanges, they should have our Q:Lines anyway to enforce themselves. * -- w00t */ if (!allowinvalid || !IS_LOCAL(user)) { XLine* mq = ServerInstance->XLines->MatchesLine("Q",parameters[0]); if (mq) { if (user->registered == REG_ALL) { ServerInstance->SNO->WriteToSnoMask('x', "Q-Lined nickname %s from %s!%s@%s: %s", parameters[0].c_str(), user->nick.c_str(), user->ident.c_str(), user->host.c_str(), mq->reason); } user->WriteNumeric(432, "%s %s :Invalid nickname: %s",user->nick.c_str(), parameters[0].c_str(), mq->reason); return CMD_FAILURE; } if (ServerInstance->Config->RestrictBannedUsers) { for (UCListIter i = user->chans.begin(); i != user->chans.end(); i++) { Channel *chan = i->first; if (chan->GetStatus(user) < STATUS_VOICE && chan->IsBanned(user)) { user->WriteNumeric(404, "%s %s :Cannot send to channel (you're banned)", user->nick.c_str(), chan->name.c_str()); return CMD_FAILURE; } } } } /* * Uh oh.. if the nickname is in use, and it's not in use by the person using it (doh) -- * then we have a potential collide. Check whether someone else is camping on the nick * (i.e. connect -> send NICK, don't send USER.) If they are camping, force-change the * camper to their UID, and allow the incoming nick change. * * If the guy using the nick is already using it, tell the incoming nick change to gtfo, * because the nick is already (rightfully) in use. -- w00t */ User* InUse = ServerInstance->FindNickOnly(parameters[0]); if (InUse && (InUse != user)) { if (InUse->registered != REG_ALL) { /* force the camper to their UUID, and ask them to re-send a NICK. */ InUse->WriteTo(InUse, "NICK %s", InUse->uuid.c_str()); InUse->WriteNumeric(433, "%s %s :Nickname overruled.", InUse->nick.c_str(), InUse->nick.c_str()); InUse->UpdateNickHash(InUse->uuid.c_str()); InUse->nick.assign(InUse->uuid, 0, IS_LOCAL(InUse) ? ServerInstance->Config->Limits.NickMax : MAXBUF); InUse->InvalidateCache(); InUse->registered &= ~REG_NICK; } else { /* No camping, tell the incoming user to stop trying to change nick ;p */ user->WriteNumeric(433, "%s %s :Nickname is already in use.", user->registered >= REG_NICK ? user->nick.c_str() : "*", parameters[0].c_str()); return CMD_FAILURE; } } } int MOD_RESULT = 0; FOREACH_RESULT(I_OnUserPreNick,OnUserPreNick(user, parameters[0])); if (MOD_RESULT) // if a module returns true, the nick change is silently forbidden. return CMD_FAILURE; if (user->registered == REG_ALL) user->WriteCommon("NICK %s", parameters[0].c_str()); oldnick.assign(user->nick, 0, IS_LOCAL(user) ? ServerInstance->Config->Limits.NickMax : MAXBUF); /* change the nick of the user in the users_hash */ user = user->UpdateNickHash(parameters[0].c_str()); /* actually change the nick within the record */ if (!user) return CMD_FAILURE; user->nick.assign(parameters[0], 0, IS_LOCAL(user) ? ServerInstance->Config->Limits.NickMax : MAXBUF); user->InvalidateCache(); /* Update display nicks */ for (UCListIter v = user->chans.begin(); v != user->chans.end(); v++) { CUList* ulist = v->first->GetUsers(); CUList::iterator i = ulist->find(user); if (i != ulist->end()) i->second = user->nick; } if (user->registered < REG_NICKUSER) { user->registered = (user->registered | REG_NICK); if (user->registered == REG_NICKUSER) { /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */ MOD_RESULT = 0; FOREACH_RESULT(I_OnUserRegister,OnUserRegister(user)); if (MOD_RESULT > 0) return CMD_FAILURE; // return early to not penalize new users return CMD_SUCCESS; } } if (user->registered == REG_ALL) { user->IncreasePenalty(10); FOREACH_MOD(I_OnUserPostNick,OnUserPostNick(user, oldnick)); } return CMD_SUCCESS; }