/** Handle /AWAY */ CmdResult CommandAway::Handle (const std::vector<std::string>& parameters, User *user) { ModResult MOD_RESULT; if ((parameters.size()) && (!parameters[0].empty())) { FIRST_MOD_RESULT(OnSetAway, MOD_RESULT, (user, parameters[0])); if (MOD_RESULT == MOD_RES_DENY && IS_LOCAL(user)) return CMD_FAILURE; user->awaytime = ServerInstance->Time(); user->awaymsg.assign(parameters[0], 0, ServerInstance->Config->Limits.MaxAway); user->WriteNumeric(RPL_NOWAWAY, ":You have been marked as being away"); } else { FIRST_MOD_RESULT(OnSetAway, MOD_RESULT, (user, "")); if (MOD_RESULT == MOD_RES_DENY && IS_LOCAL(user)) return CMD_FAILURE; user->awaymsg.clear(); user->WriteNumeric(RPL_UNAWAY, ":You are no longer marked as being away"); } return CMD_SUCCESS; }
bool User::ChangeDisplayedHost(const char* shost) { if (dhost == shost) return true; if (IS_LOCAL(this)) { ModResult MOD_RESULT; FIRST_MOD_RESULT(OnChangeLocalUserHost, MOD_RESULT, (IS_LOCAL(this),shost)); if (MOD_RESULT == MOD_RES_DENY) return false; } FOREACH_MOD(I_OnChangeHost, OnChangeHost(this,shost)); std::string quitstr = ":" + GetFullHost() + " QUIT :Changing host"; /* Fix by Om: User::dhost is 65 long, this was truncating some long hosts */ this->dhost.assign(shost, 0, 64); this->InvalidateCache(); this->DoHostCycle(quitstr); if (IS_LOCAL(this)) this->WriteNumeric(RPL_YOURDISPLAYEDHOST, "%s %s :is now your displayed host",this->nick.c_str(),this->dhost.c_str()); return true; }
std::string& ModeChannelBan::DelBan(User *user, std::string& dest, Channel *chan, int) { if ((!user) || (!chan)) { ServerInstance->Logs->Log("MODE",DEFAULT,"*** BUG *** TakeBan was given an invalid parameter"); dest.clear(); return dest; } for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++) { if (!strcasecmp(i->data.c_str(), dest.c_str())) { ModResult MOD_RESULT; FIRST_MOD_RESULT(OnDelBan, MOD_RESULT, (user, chan, dest)); if (MOD_RESULT == MOD_RES_DENY) { dest.clear(); return dest; } chan->bans.erase(i); return dest; } } dest.clear(); return dest; }
void ModeParser::ShowListModeList(User* user, Channel* chan, ModeHandler* mh) { { ModResult MOD_RESULT; FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, mh, "", true)); if (MOD_RESULT == MOD_RES_DENY) return; bool display = true; // Ask mode watchers whether it's OK to show the list std::pair<ModeWatcherMap::iterator, ModeWatcherMap::iterator> itpair = modewatchermap.equal_range(mh->name); for (ModeWatcherMap::iterator i = itpair.first; i != itpair.second; ++i) { ModeWatcher* mw = i->second; if (mw->GetModeType() == MODETYPE_CHANNEL) { std::string dummyparam; if (!mw->BeforeMode(user, NULL, chan, dummyparam, true)) { // A mode watcher doesn't want us to show the list display = false; break; } } } if (display) mh->DisplayList(user, chan); else mh->DisplayEmptyList(user, chan); } }
void ModeParser::DisplayListModes(User* user, Channel* chan, std::string &mode_sequence) { seq++; for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++) { unsigned char mletter = *letter; if (mletter == '+') continue; /* Ensure the user doesnt request the same mode twice, * so they cant flood themselves off out of idiocy. */ if (sent[mletter] == seq) continue; sent[mletter] = seq; ModeHandler *mh = this->FindMode(mletter, MODETYPE_CHANNEL); if (!mh || !mh->IsListMode()) return; ModResult MOD_RESULT; FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, mletter, "", true, 0)); if (MOD_RESULT == MOD_RES_DENY) continue; bool display = true; if (!user->HasPrivPermission("channels/auspex") && ServerInstance->Config->HideModeLists[mletter] && (chan->GetPrefixValue(user) < HALFOP_VALUE)) { user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You do not have access to view the +%c list", user->nick.c_str(), chan->name.c_str(), mletter); display = false; } // Ask mode watchers whether it's OK to show the list std::pair<ModeWatchIter, ModeWatchIter> itpair = modewatchermap.equal_range(mh->name); for (ModeWatchIter i = itpair.first; i != itpair.second; ++i) { ModeWatcher* mw = i->second; if (mw->GetModeType() == MODETYPE_CHANNEL) { std::string dummyparam; if (!mw->BeforeMode(user, NULL, chan, dummyparam, true)) { // A mode watcher doesn't want us to show the list display = false; break; } } } if (display) mh->DisplayList(user, chan); else mh->DisplayEmptyList(user, chan); } }
void InspIRCd::SendWhoisLine(User* user, User* dest, int numeric, const std::string &text) { std::string copy_text = text; ModResult MOD_RESULT; FIRST_MOD_RESULT(OnWhoisLine, MOD_RESULT, (user, dest, numeric, copy_text)); if (MOD_RESULT != MOD_RES_DENY) user->WriteServ("%d %s", numeric, copy_text.c_str()); }
CmdResult CommandTopic::HandleLocal(const std::vector<std::string>& parameters, LocalUser* user) { Channel* c = ServerInstance->FindChan(parameters[0]); if (!c) { user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str()); return CMD_FAILURE; } if (parameters.size() == 1) { if (c) { if ((c->IsModeSet(secretmode)) && (!c->HasUser(user))) { user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", c->name.c_str()); return CMD_FAILURE; } if (c->topic.length()) { user->WriteNumeric(RPL_TOPIC, "%s :%s", c->name.c_str(), c->topic.c_str()); user->WriteNumeric(RPL_TOPICTIME, "%s %s %lu", c->name.c_str(), c->setby.c_str(), (unsigned long)c->topicset); } else { user->WriteNumeric(RPL_NOTOPICSET, "%s :No topic is set.", c->name.c_str()); } } return CMD_SUCCESS; } std::string t = parameters[1]; // needed, in case a module wants to change it ModResult res; FIRST_MOD_RESULT(OnPreTopicChange, res, (user,c,t)); if (res == MOD_RES_DENY) return CMD_FAILURE; if (res != MOD_RES_ALLOW) { if (!c->HasUser(user)) { user->WriteNumeric(ERR_NOTONCHANNEL, "%s :You're not on that channel!", c->name.c_str()); return CMD_FAILURE; } if (c->IsModeSet(topiclockmode) && !ServerInstance->OnCheckExemption(user, c, "topiclock").check(c->GetPrefixValue(user) >= HALFOP_VALUE)) { user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s :You do not have access to change the topic on this channel", c->name.c_str()); return CMD_FAILURE; } } c->SetTopic(user, t); return CMD_SUCCESS; }
void User::WriteNumeric(unsigned int numeric, const std::string &text) { char textbuffer[MAXBUF]; ModResult MOD_RESULT; FIRST_MOD_RESULT(OnNumeric, MOD_RESULT, (this, numeric, text)); if (MOD_RESULT == MOD_RES_DENY) return; snprintf(textbuffer,MAXBUF,":%s %03u %s",ServerInstance->Config->ServerName.c_str(), numeric, text.c_str()); this->Write(std::string(textbuffer)); }
void User::WriteNumeric(unsigned int numeric, const std::string &text) { ModResult MOD_RESULT; FIRST_MOD_RESULT(OnNumeric, MOD_RESULT, (this, numeric, text)); if (MOD_RESULT == MOD_RES_DENY) return; const std::string message = InspIRCd::Format(":%s %03u %s %s", ServerInstance->Config->ServerName.c_str(), numeric, this->registered & REG_NICK ? this->nick.c_str() : "*", text.c_str()); this->Write(message); }
CmdResult CommandUser::CheckRegister(LocalUser* user) { // If the user is registered, return CMD_SUCCESS/CMD_FAILURE depending on what modules say, otherwise just // return CMD_SUCCESS without doing anything, knowing the other handler will call us again if (user->registered == REG_NICKUSER) { ModResult MOD_RESULT; FIRST_MOD_RESULT(OnUserRegister, MOD_RESULT, (user)); if (MOD_RESULT == MOD_RES_DENY) return CMD_FAILURE; } return CMD_SUCCESS; }
void CommandWho::SendWhoLine(User* user, const std::vector<std::string>& parms, Membership* memb, User* u, std::vector<Numeric::Numeric>& whoresults) { if (!memb) memb = get_first_visible_channel(u); Numeric::Numeric wholine(RPL_WHOREPLY); wholine.push(memb ? memb->chan->name : "*").push(u->ident); wholine.push(opt_showrealhost ? u->host : u->dhost); if (!ServerInstance->Config->HideWhoisServer.empty() && !user->HasPrivPermission("servers/auspex")) wholine.push(ServerInstance->Config->HideWhoisServer); else wholine.push(u->server->GetName()); wholine.push(u->nick); std::string param; /* away? */ if (u->IsAway()) { param.push_back('G'); } else { param.push_back('H'); } /* oper? */ if (u->IsOper()) { param.push_back('*'); } if (memb) { char prefix = memb->GetPrefixChar(); if (prefix) param.push_back(prefix); } wholine.push(param); wholine.push("0 "); wholine.GetParams().back().append(u->fullname); ModResult res; FIRST_MOD_RESULT(OnSendWhoLine, res, (user, parms, u, memb, wholine)); if (res != MOD_RES_DENY) whoresults.push_back(wholine); }
std::string& ModeChannelBan::AddBan(User *user, std::string &dest, Channel *chan, int) { if ((!user) || (!chan)) { ServerInstance->Logs->Log("MODE",DEFAULT,"*** BUG *** AddBan was given an invalid parameter"); dest.clear(); return dest; } /* Attempt to tidy the mask */ ModeParser::CleanMask(dest); /* If the mask was invalid, we exit */ if (dest.empty() || dest.length() > 250) return dest; long maxbans = chan->GetMaxBans(); if (IS_LOCAL(user) && ((unsigned)chan->bans.size() >= (unsigned)maxbans)) { user->WriteServ("478 %s %s :Channel ban list for %s is full (maximum entries for this channel is %ld)",user->nick.c_str(), chan->name.c_str(), chan->name.c_str(), maxbans); dest.clear(); return dest; } ModResult MOD_RESULT; FIRST_MOD_RESULT(OnAddBan, MOD_RESULT, (user,chan,dest)); if (MOD_RESULT == MOD_RES_DENY) { dest.clear(); return dest; } for (BanList::iterator i = chan->bans.begin(); i != chan->bans.end(); i++) { if (i->data == dest) { /* dont allow a user to set the same ban twice */ dest.clear(); return dest; } } b.set_time = ServerInstance->Time(); b.data.assign(dest, 0, MAXBUF); b.set_by.assign(user->nick, 0, 64); chan->bans.push_back(b); return dest; }
bool User::ChangeName(const std::string& gecos) { if (!this->fullname.compare(gecos)) return true; if (IS_LOCAL(this)) { ModResult MOD_RESULT; FIRST_MOD_RESULT(OnChangeLocalUserGECOS, MOD_RESULT, (IS_LOCAL(this),gecos)); if (MOD_RESULT == MOD_RES_DENY) return false; FOREACH_MOD(OnChangeName, (this,gecos)); } this->fullname.assign(gecos, 0, ServerInstance->Config->Limits.MaxGecos); return true; }
CmdResult CommandUser::HandleLocal(const std::vector<std::string>& parameters, LocalUser *user) { /* A user may only send the USER command once */ if (!(user->registered & REG_USER)) { if (!ServerInstance->IsIdent(parameters[0])) { /* * RFC says we must use this numeric, so we do. Let's make it a little more nub friendly though. :) * -- Craig, and then w00t. */ user->WriteNumeric(ERR_NEEDMOREPARAMS, "USER :Your username is not valid"); return CMD_FAILURE; } else { /* * The ident field is IDENTMAX+2 in size to account for +1 for the optional * ~ character, and +1 for null termination, therefore we can safely use up to * IDENTMAX here. */ user->ChangeIdent(parameters[0]); user->fullname.assign(parameters[3].empty() ? "No info" : parameters[3], 0, ServerInstance->Config->Limits.MaxGecos); user->registered = (user->registered | REG_USER); } } else { user->WriteNumeric(ERR_ALREADYREGISTERED, ":You may not reregister"); return CMD_FAILURE; } /* parameters 2 and 3 are local and remote hosts, and are ignored */ if (user->registered == REG_NICKUSER) { ModResult MOD_RESULT; /* user is registered now, bit 0 = USER command, bit 1 = sent a NICK command */ FIRST_MOD_RESULT(OnUserRegister, MOD_RESULT, (user)); if (MOD_RESULT == MOD_RES_DENY) return CMD_FAILURE; } return CMD_SUCCESS; }
void Channel::CheckDestroy() { if (!userlist.empty()) return; ModResult res; FIRST_MOD_RESULT(OnChannelPreDelete, res, (this)); if (res == MOD_RES_DENY) return; // If the channel isn't in chanlist then it is already in the cull list, don't add it again chan_hash::iterator iter = ServerInstance->chanlist.find(this->name); if ((iter == ServerInstance->chanlist.end()) || (iter->second != this)) return; FOREACH_MOD(OnChannelDelete, (this)); ServerInstance->chanlist.erase(iter); ServerInstance->GlobalCulls.AddItem(this); }
bool InspIRCd::PassCompare(Extensible* ex, const std::string& data, const std::string& input, const std::string& hashtype) { ModResult res; FIRST_MOD_RESULT(OnPassCompare, res, (ex, data, input, hashtype)); /* Module matched */ if (res == MOD_RES_ALLOW) return true; /* Module explicitly didnt match */ if (res == MOD_RES_DENY) return false; /* We dont handle any hash types except for plaintext - Thanks tra26 */ if (!hashtype.empty() && hashtype != "plaintext") return false; return (data == input); }
int InspIRCd::PassCompare(Extensible* ex, const std::string &data, const std::string &input, const std::string &hashtype) { ModResult res; FIRST_MOD_RESULT(OnPassCompare, res, (ex, data, input, hashtype)); /* Module matched */ if (res == MOD_RES_ALLOW) return 0; /* Module explicitly didnt match */ if (res == MOD_RES_DENY) return 1; /* We dont handle any hash types except for plaintext - Thanks tra26 */ if (!hashtype.empty() && hashtype != "plaintext") /* See below. 1 because they dont match */ return 1; return (data != input); // this seems back to front, but returns 0 if they *match*, 1 else }
void Channel::CheckDestroy() { if (!userlist.empty()) return; ModResult res; FIRST_MOD_RESULT(OnChannelPreDelete, res, (this)); if (res == MOD_RES_DENY) return; chan_hash::iterator iter = ServerInstance->chanlist->find(this->name); /* kill the record */ if (iter != ServerInstance->chanlist->end()) { FOREACH_MOD(OnChannelDelete, (this)); ServerInstance->chanlist->erase(iter); } ClearInvites(); ServerInstance->GlobalCulls.AddItem(this); }
bool User::ChangeDisplayedHost(const std::string& shost) { if (dhost == shost) return true; if (IS_LOCAL(this)) { ModResult MOD_RESULT; FIRST_MOD_RESULT(OnChangeLocalUserHost, MOD_RESULT, (IS_LOCAL(this),shost)); if (MOD_RESULT == MOD_RES_DENY) return false; } FOREACH_MOD(OnChangeHost, (this,shost)); this->dhost.assign(shost, 0, ServerInstance->Config->Limits.MaxHost); this->InvalidateCache(); if (IS_LOCAL(this)) this->WriteNumeric(RPL_YOURDISPLAYEDHOST, "%s :is now your displayed host", this->dhost.c_str()); return true; }
void ModeParser::Process(const std::vector<std::string>& parameters, User* user, ModeProcessFlag flags) { std::string target = parameters[0]; Channel* targetchannel = ServerInstance->FindChan(target); User* targetuser = ServerInstance->FindNick(target); ModeType type = targetchannel ? MODETYPE_CHANNEL : MODETYPE_USER; LastParse.clear(); LastParseParams.clear(); LastParseTranslate.clear(); if ((!targetchannel) && ((!targetuser) || (IS_SERVER(targetuser)))) { user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(),target.c_str()); return; } if (parameters.size() == 1) { this->DisplayCurrentModes(user, targetuser, targetchannel, target.c_str()); return; } ModResult MOD_RESULT; FIRST_MOD_RESULT(OnPreMode, MOD_RESULT, (user, targetuser, targetchannel, parameters)); bool SkipAccessChecks = false; if (!IS_LOCAL(user) || ServerInstance->ULine(user->server) || MOD_RESULT == MOD_RES_ALLOW) SkipAccessChecks = true; else if (MOD_RESULT == MOD_RES_DENY) return; if (targetuser && !SkipAccessChecks && user != targetuser) { user->WriteNumeric(ERR_USERSDONTMATCH, "%s :Can't change mode for other users", user->nick.c_str()); return; } std::string mode_sequence = parameters[1]; std::string output_mode; std::ostringstream output_parameters; LastParseParams.push_back(output_mode); LastParseTranslate.push_back(TR_TEXT); bool adding = true; char output_pm = '\0'; // current output state, '+' or '-' unsigned int param_at = 2; for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++) { unsigned char modechar = *letter; if (modechar == '+' || modechar == '-') { adding = (modechar == '+'); continue; } ModeHandler *mh = this->FindMode(modechar, type); if (!mh) { /* No mode handler? Unknown mode character then. */ user->WriteServ("%d %s %c :is unknown mode char to me", type == MODETYPE_CHANNEL ? 472 : 501, user->nick.c_str(), modechar); continue; } std::string parameter; int pcnt = mh->GetNumParams(adding); if (pcnt && param_at == parameters.size()) { /* No parameter, continue to the next mode */ mh->OnParameterMissing(user, targetuser, targetchannel); continue; } else if (pcnt) { parameter = parameters[param_at++]; /* Make sure the user isn't trying to slip in an invalid parameter */ if ((parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos)) continue; if ((flags & MODE_MERGE) && targetchannel && targetchannel->IsModeSet(mh) && !mh->IsListMode()) { std::string ours = targetchannel->GetModeParameter(mh); if (!mh->ResolveModeConflict(parameter, ours, targetchannel)) /* we won the mode merge, don't apply this mode */ continue; } } ModeAction ma = TryMode(user, targetuser, targetchannel, adding, modechar, parameter, SkipAccessChecks); if (ma != MODEACTION_ALLOW) continue; char needed_pm = adding ? '+' : '-'; if (needed_pm != output_pm) { output_pm = needed_pm; output_mode.append(1, output_pm); } output_mode.append(1, modechar); if (pcnt) { output_parameters << " " << parameter; LastParseParams.push_back(parameter); LastParseTranslate.push_back(mh->GetTranslateType()); } if ( (output_mode.length() + output_parameters.str().length() > 450) || (output_mode.length() > 100) || (LastParseParams.size() > ServerInstance->Config->Limits.MaxModes)) { /* mode sequence is getting too long */ break; } } LastParseParams[0] = output_mode; if (!output_mode.empty()) { LastParse = targetchannel ? targetchannel->name : targetuser->nick; LastParse.append(" "); LastParse.append(output_mode); LastParse.append(output_parameters.str()); if (!(flags & MODE_LOCALONLY)) ServerInstance->PI->SendMode(user, targetuser, targetchannel, LastParseParams, LastParseTranslate); if (targetchannel) targetchannel->WriteChannel(user, "MODE " + LastParse); else targetuser->WriteFrom(user, "MODE " + LastParse); FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastParseParams, LastParseTranslate)); } else if (targetchannel && parameters.size() == 2) { /* Special case for displaying the list for listmodes, * e.g. MODE #chan b, or MODE #chan +b without a parameter */ this->DisplayListModes(user, targetchannel, mode_sequence); } }
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; }
/** Handle /INVITE */ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, User *user) { ModResult MOD_RESULT; if (parameters.size() == 2 || parameters.size() == 3) { User* u; if (IS_LOCAL(user)) u = ServerInstance->FindNickOnly(parameters[0]); else u = ServerInstance->FindNick(parameters[0]); Channel* c = ServerInstance->FindChan(parameters[1]); time_t timeout = 0; if (parameters.size() == 3) { if (IS_LOCAL(user)) timeout = ServerInstance->Time() + InspIRCd::Duration(parameters[1]); else timeout = ConvToInt(parameters[2]); } if ((!c) || (!u) || (u->registered != REG_ALL)) { user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), c ? parameters[0].c_str() : parameters[1].c_str()); return CMD_FAILURE; } if ((IS_LOCAL(user)) && (!c->HasUser(user))) { user->WriteNumeric(ERR_NOTONCHANNEL, "%s %s :You're not on that channel!",user->nick.c_str(), c->name.c_str()); return CMD_FAILURE; } if (c->HasUser(u)) { user->WriteNumeric(ERR_USERONCHANNEL, "%s %s %s :is already on channel",user->nick.c_str(),u->nick.c_str(),c->name.c_str()); return CMD_FAILURE; } FIRST_MOD_RESULT(OnUserPreInvite, MOD_RESULT, (user,u,c,timeout)); if (MOD_RESULT == MOD_RES_DENY) { return CMD_FAILURE; } else if (MOD_RESULT == MOD_RES_PASSTHRU) { if (IS_LOCAL(user)) { unsigned int rank = c->GetPrefixValue(user); if (rank < HALFOP_VALUE) { // Check whether halfop mode is available and phrase error message accordingly ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL); user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator", user->nick.c_str(), c->name.c_str(), (mh && mh->name == "halfop" ? "half-" : "")); return CMD_FAILURE; } } } if (IS_LOCAL(u)) { Invitation::Create(c, IS_LOCAL(u), timeout); u->WriteFrom(user,"INVITE %s :%s",u->nick.c_str(),c->name.c_str()); } if (IS_LOCAL(user)) user->WriteNumeric(RPL_INVITING, "%s %s %s",user->nick.c_str(),u->nick.c_str(),c->name.c_str()); if (ServerInstance->Config->AnnounceInvites != ServerConfig::INVITE_ANNOUNCE_NONE) { char prefix; switch (ServerInstance->Config->AnnounceInvites) { case ServerConfig::INVITE_ANNOUNCE_OPS: { prefix = '@'; break; } case ServerConfig::INVITE_ANNOUNCE_DYNAMIC: { PrefixMode* mh = ServerInstance->Modes->FindPrefixMode('h'); prefix = (mh && mh->name == "halfop" ? mh->GetPrefix() : '@'); break; } default: { prefix = 0; break; } } c->WriteAllExceptSender(user, true, prefix, "NOTICE %s :*** %s invited %s into the channel", c->name.c_str(), user->nick.c_str(), u->nick.c_str()); } FOREACH_MOD(OnUserInvite, (user,u,c,timeout)); } else if (IS_LOCAL(user)) { // pinched from ircu - invite with not enough parameters shows channels // youve been invited to but haven't joined yet. InviteList& il = IS_LOCAL(user)->GetInviteList(); for (InviteList::const_iterator i = il.begin(); i != il.end(); ++i) { user->WriteNumeric(RPL_INVITELIST, "%s :%s",user->nick.c_str(), (*i)->chan->name.c_str()); } user->WriteNumeric(RPL_ENDOFINVITELIST, "%s :End of INVITE list",user->nick.c_str()); } return CMD_SUCCESS; }
void CommandStats::DoStats(char statschar, User* user, string_list &results) { bool isPublic = ServerInstance->Config->UserStats.find(statschar) != std::string::npos; bool isRemoteOper = IS_REMOTE(user) && (user->IsOper()); bool isLocalOperWithPrivs = IS_LOCAL(user) && user->HasPrivPermission("servers/auspex"); if (!isPublic && !isRemoteOper && !isLocalOperWithPrivs) { ServerInstance->SNO->WriteToSnoMask('t', "%s '%c' denied for %s (%s@%s)", (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str()); results.push_back("481 " + user->nick + " :Permission denied - STATS " + statschar + " requires the servers/auspex priv."); return; } ModResult MOD_RESULT; FIRST_MOD_RESULT(OnStats, MOD_RESULT, (statschar, user, results)); if (MOD_RESULT == MOD_RES_DENY) { results.push_back("219 "+user->nick+" "+statschar+" :End of /STATS report"); ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)", (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str()); return; } switch (statschar) { /* stats p (show listening ports) */ case 'p': { for (std::vector<ListenSocket*>::const_iterator i = ServerInstance->ports.begin(); i != ServerInstance->ports.end(); ++i) { ListenSocket* ls = *i; std::string ip = ls->bind_addr; if (ip.empty()) ip.assign("*"); std::string type = ls->bind_tag->getString("type", "clients"); std::string hook = ls->bind_tag->getString("ssl", "plaintext"); results.push_back("249 "+user->nick+" :"+ ip + ":"+ConvToStr(ls->bind_port)+ " (" + type + ", " + hook + ")"); } } break; /* These stats symbols must be handled by a linking module */ case 'n': case 'c': break; case 'i': { for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++) { ConnectClass* c = *i; std::stringstream res; res << "215 " << user->nick << " I " << c->name << ' '; if (c->type == CC_ALLOW) res << '+'; if (c->type == CC_DENY) res << '-'; if (c->type == CC_NAMED) res << '*'; else res << c->host; res << ' ' << c->config->getString("port", "*") << ' '; res << c->GetRecvqMax() << ' ' << c->GetSendqSoftMax() << ' ' << c->GetSendqHardMax() << ' ' << c->GetCommandRate() << ' ' << c->GetPenaltyThreshold(); if (c->fakelag) res << '*'; results.push_back(res.str()); } } break; case 'Y': { int idx = 0; for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++) { ConnectClass* c = *i; results.push_back("215 "+user->nick+" i NOMATCH * "+c->GetHost()+" "+ConvToStr(c->limit ? c->limit : SocketEngine::GetMaxFds())+" "+ConvToStr(idx)+" "+ServerInstance->Config->ServerName+" *"); results.push_back("218 "+user->nick+" Y "+ConvToStr(idx)+" "+ConvToStr(c->GetPingTime())+" 0 "+ConvToStr(c->GetSendqHardMax())+" :"+ ConvToStr(c->GetRecvqMax())+" "+ConvToStr(c->GetRegTimeout())); idx++; } } break; case 'P': { unsigned int idx = 0; const UserManager::OperList& opers = ServerInstance->Users->all_opers; for (UserManager::OperList::const_iterator i = opers.begin(); i != opers.end(); ++i) { User* oper = *i; if (!oper->server->IsULine()) { LocalUser* lu = IS_LOCAL(oper); results.push_back("249 " + user->nick + " :" + oper->nick + " (" + oper->ident + "@" + oper->dhost + ") Idle: " + (lu ? ConvToStr(ServerInstance->Time() - lu->idle_lastmsg) + " secs" : "unavailable")); idx++; } } results.push_back("249 "+user->nick+" :"+ConvToStr(idx)+" OPER(s)"); } break; case 'k': ServerInstance->XLines->InvokeStats("K",216,user,results); break; case 'g': ServerInstance->XLines->InvokeStats("G",223,user,results); break; case 'q': ServerInstance->XLines->InvokeStats("Q",217,user,results); break; case 'Z': ServerInstance->XLines->InvokeStats("Z",223,user,results); break; case 'e': ServerInstance->XLines->InvokeStats("E",223,user,results); break; case 'E': { const SocketEngine::Statistics& stats = SocketEngine::GetStats(); results.push_back("249 "+user->nick+" :Total events: "+ConvToStr(stats.TotalEvents)); results.push_back("249 "+user->nick+" :Read events: "+ConvToStr(stats.ReadEvents)); results.push_back("249 "+user->nick+" :Write events: "+ConvToStr(stats.WriteEvents)); results.push_back("249 "+user->nick+" :Error events: "+ConvToStr(stats.ErrorEvents)); break; } /* stats m (list number of times each command has been used, plus bytecount) */ case 'm': for (Commandtable::iterator i = ServerInstance->Parser->cmdlist.begin(); i != ServerInstance->Parser->cmdlist.end(); i++) { if (i->second->use_count) { /* RPL_STATSCOMMANDS */ results.push_back("212 "+user->nick+" "+i->second->name+" "+ConvToStr(i->second->use_count)); } } break; /* stats z (debug and memory info) */ case 'z': { results.push_back("249 "+user->nick+" :Users: "+ConvToStr(ServerInstance->Users->GetUsers().size())); results.push_back("249 "+user->nick+" :Channels: "+ConvToStr(ServerInstance->GetChans().size())); results.push_back("249 "+user->nick+" :Commands: "+ConvToStr(ServerInstance->Parser->cmdlist.size())); float kbitpersec_in, kbitpersec_out, kbitpersec_total; char kbitpersec_in_s[30], kbitpersec_out_s[30], kbitpersec_total_s[30]; SocketEngine::GetStats().GetBandwidth(kbitpersec_in, kbitpersec_out, kbitpersec_total); snprintf(kbitpersec_total_s, 30, "%03.5f", kbitpersec_total); snprintf(kbitpersec_out_s, 30, "%03.5f", kbitpersec_out); snprintf(kbitpersec_in_s, 30, "%03.5f", kbitpersec_in); results.push_back("249 "+user->nick+" :Bandwidth total: "+ConvToStr(kbitpersec_total_s)+" kilobits/sec"); results.push_back("249 "+user->nick+" :Bandwidth out: "+ConvToStr(kbitpersec_out_s)+" kilobits/sec"); results.push_back("249 "+user->nick+" :Bandwidth in: "+ConvToStr(kbitpersec_in_s)+" kilobits/sec"); #ifndef _WIN32 /* Moved this down here so all the not-windows stuff (look w00tie, I didn't say win32!) is in one ifndef. * Also cuts out some identical code in both branches of the ifndef. -- Om */ rusage R; /* Not sure why we were doing '0' with a RUSAGE_SELF comment rather than just using RUSAGE_SELF -- Om */ if (!getrusage(RUSAGE_SELF,&R)) /* RUSAGE_SELF */ { results.push_back("249 "+user->nick+" :Total allocation: "+ConvToStr(R.ru_maxrss)+"K"); results.push_back("249 "+user->nick+" :Signals: "+ConvToStr(R.ru_nsignals)); results.push_back("249 "+user->nick+" :Page faults: "+ConvToStr(R.ru_majflt)); results.push_back("249 "+user->nick+" :Swaps: "+ConvToStr(R.ru_nswap)); results.push_back("249 "+user->nick+" :Context Switches: Voluntary; "+ConvToStr(R.ru_nvcsw)+" Involuntary; "+ConvToStr(R.ru_nivcsw)); char percent[30]; float n_elapsed = (ServerInstance->Time() - ServerInstance->stats->LastSampled.tv_sec) * 1000000 + (ServerInstance->Time_ns() - ServerInstance->stats->LastSampled.tv_nsec) / 1000; float n_eaten = ((R.ru_utime.tv_sec - ServerInstance->stats->LastCPU.tv_sec) * 1000000 + R.ru_utime.tv_usec - ServerInstance->stats->LastCPU.tv_usec); float per = (n_eaten / n_elapsed) * 100; snprintf(percent, 30, "%03.5f%%", per); results.push_back("249 "+user->nick+" :CPU Use (now): "+percent); n_elapsed = ServerInstance->Time() - ServerInstance->startup_time; n_eaten = (float)R.ru_utime.tv_sec + R.ru_utime.tv_usec / 100000.0; per = (n_eaten / n_elapsed) * 100; snprintf(percent, 30, "%03.5f%%", per); results.push_back("249 "+user->nick+" :CPU Use (total): "+percent); } #else PROCESS_MEMORY_COUNTERS MemCounters; if (GetProcessMemoryInfo(GetCurrentProcess(), &MemCounters, sizeof(MemCounters))) { results.push_back("249 "+user->nick+" :Total allocation: "+ConvToStr((MemCounters.WorkingSetSize + MemCounters.PagefileUsage) / 1024)+"K"); results.push_back("249 "+user->nick+" :Pagefile usage: "+ConvToStr(MemCounters.PagefileUsage / 1024)+"K"); results.push_back("249 "+user->nick+" :Page faults: "+ConvToStr(MemCounters.PageFaultCount)); } FILETIME CreationTime; FILETIME ExitTime; FILETIME KernelTime; FILETIME UserTime; LARGE_INTEGER ThisSample; if(GetProcessTimes(GetCurrentProcess(), &CreationTime, &ExitTime, &KernelTime, &UserTime) && QueryPerformanceCounter(&ThisSample)) { KernelTime.dwHighDateTime += UserTime.dwHighDateTime; KernelTime.dwLowDateTime += UserTime.dwLowDateTime; double n_eaten = (double)( ( (uint64_t)(KernelTime.dwHighDateTime - ServerInstance->stats->LastCPU.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime - ServerInstance->stats->LastCPU.dwLowDateTime) )/100000; double n_elapsed = (double)(ThisSample.QuadPart - ServerInstance->stats->LastSampled.QuadPart) / ServerInstance->stats->QPFrequency.QuadPart; double per = (n_eaten/n_elapsed); char percent[30]; snprintf(percent, 30, "%03.5f%%", per); results.push_back("249 "+user->nick+" :CPU Use (now): "+percent); n_elapsed = ServerInstance->Time() - ServerInstance->startup_time; n_eaten = (double)(( (uint64_t)(KernelTime.dwHighDateTime) << 32 ) + (uint64_t)(KernelTime.dwLowDateTime))/100000; per = (n_eaten / n_elapsed); snprintf(percent, 30, "%03.5f%%", per); results.push_back("249 "+user->nick+" :CPU Use (total): "+percent); } #endif } break; case 'T': { results.push_back("249 "+user->nick+" :accepts "+ConvToStr(ServerInstance->stats->statsAccept)+" refused "+ConvToStr(ServerInstance->stats->statsRefused)); results.push_back("249 "+user->nick+" :unknown commands "+ConvToStr(ServerInstance->stats->statsUnknown)); results.push_back("249 "+user->nick+" :nick collisions "+ConvToStr(ServerInstance->stats->statsCollisions)); results.push_back("249 "+user->nick+" :dns requests "+ConvToStr(ServerInstance->stats->statsDnsGood+ServerInstance->stats->statsDnsBad)+" succeeded "+ConvToStr(ServerInstance->stats->statsDnsGood)+" failed "+ConvToStr(ServerInstance->stats->statsDnsBad)); results.push_back("249 "+user->nick+" :connection count "+ConvToStr(ServerInstance->stats->statsConnects)); results.push_back(InspIRCd::Format("249 %s :bytes sent %5.2fK recv %5.2fK", user->nick.c_str(), ServerInstance->stats->statsSent / 1024.0, ServerInstance->stats->statsRecv / 1024.0)); } break; /* stats o */ case 'o': { ConfigTagList tags = ServerInstance->Config->ConfTags("oper"); for(ConfigIter i = tags.first; i != tags.second; ++i) { ConfigTag* tag = i->second; results.push_back("243 "+user->nick+" O "+tag->getString("host")+" * "+ tag->getString("name") + " " + tag->getString("type")+" 0"); } } break; case 'O': { for (OperIndex::const_iterator i = ServerInstance->Config->OperTypes.begin(); i != ServerInstance->Config->OperTypes.end(); ++i) { OperInfo* tag = i->second; tag->init(); std::string umodes; std::string cmodes; for(char c='A'; c < 'z'; c++) { ModeHandler* mh = ServerInstance->Modes->FindMode(c, MODETYPE_USER); if (mh && mh->NeedsOper() && tag->AllowedUserModes[c - 'A']) umodes.push_back(c); mh = ServerInstance->Modes->FindMode(c, MODETYPE_CHANNEL); if (mh && mh->NeedsOper() && tag->AllowedChanModes[c - 'A']) cmodes.push_back(c); } results.push_back("243 "+user->nick+" O "+tag->name.c_str() + " " + umodes + " " + cmodes); } } break; /* stats l (show user I/O stats) */ case 'l': results.push_back("211 "+user->nick+" :nick[ident@host] sendq cmds_out bytes_out cmds_in bytes_in time_open"); for (LocalUserList::iterator n = ServerInstance->Users->local_users.begin(); n != ServerInstance->Users->local_users.end(); n++) { LocalUser* i = *n; results.push_back("211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->dhost+"] "+ConvToStr(i->eh.getSendQSize())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->age)); } break; /* stats L (show user I/O stats with IP addresses) */ case 'L': results.push_back("211 "+user->nick+" :nick[ident@ip] sendq cmds_out bytes_out cmds_in bytes_in time_open"); for (LocalUserList::iterator n = ServerInstance->Users->local_users.begin(); n != ServerInstance->Users->local_users.end(); n++) { LocalUser* i = *n; results.push_back("211 "+user->nick+" "+i->nick+"["+i->ident+"@"+i->GetIPString()+"] "+ConvToStr(i->eh.getSendQSize())+" "+ConvToStr(i->cmds_out)+" "+ConvToStr(i->bytes_out)+" "+ConvToStr(i->cmds_in)+" "+ConvToStr(i->bytes_in)+" "+ConvToStr(ServerInstance->Time() - i->age)); } break; /* stats u (show server uptime) */ case 'u': { time_t current_time = 0; current_time = ServerInstance->Time(); time_t server_uptime = current_time - ServerInstance->startup_time; struct tm* stime; stime = gmtime(&server_uptime); /* i dont know who the hell would have an ircd running for over a year nonstop, but * Craig suggested this, and it seemed a good idea so in it went */ if (stime->tm_year > 70) { results.push_back(InspIRCd::Format("242 %s :Server up %d years, %d days, %.2d:%.2d:%.2d", user->nick.c_str(), stime->tm_year - 70, stime->tm_yday, stime->tm_hour, stime->tm_min, stime->tm_sec)); } else { results.push_back(InspIRCd::Format("242 %s :Server up %d days, %.2d:%.2d:%.2d", user->nick.c_str(), stime->tm_yday, stime->tm_hour, stime->tm_min, stime->tm_sec)); } } break; default: break; } results.push_back("219 "+user->nick+" "+statschar+" :End of /STATS report"); ServerInstance->SNO->WriteToSnoMask('t',"%s '%c' requested by %s (%s@%s)", (IS_LOCAL(user) ? "Stats" : "Remote stats"), statschar, user->nick.c_str(), user->ident.c_str(), user->host.c_str()); return; }
void LocalUser::FullConnect() { ServerInstance->stats.Connects++; this->idle_lastmsg = ServerInstance->Time(); /* * You may be thinking "wtf, we checked this in User::AddClient!" - and yes, we did, BUT. * At the time AddClient is called, we don't have a resolved host, by here we probably do - which * may put the user into a totally seperate class with different restrictions! so we *must* check again. * Don't remove this! -- w00t */ MyClass = NULL; SetClass(); CheckClass(); CheckLines(); if (quitting) return; this->WriteNumeric(RPL_WELCOME, ":Welcome to the %s IRC Network %s", ServerInstance->Config->Network.c_str(), GetFullRealHost().c_str()); this->WriteNumeric(RPL_YOURHOSTIS, ":Your host is %s, running version %s", ServerInstance->Config->ServerName.c_str(), INSPIRCD_BRANCH); this->WriteNumeric(RPL_SERVERCREATED, ":This server was created %s %s", __TIME__, __DATE__); const std::string& modelist = ServerInstance->Modes->GetModeListFor004Numeric(); this->WriteNumeric(RPL_SERVERVERSION, "%s %s %s", ServerInstance->Config->ServerName.c_str(), INSPIRCD_BRANCH, modelist.c_str()); ServerInstance->ISupport.SendTo(this); /* Now registered */ if (ServerInstance->Users->unregistered_count) ServerInstance->Users->unregistered_count--; /* Trigger MOTD and LUSERS output, give modules a chance too */ ModResult MOD_RESULT; std::string command("LUSERS"); std::vector<std::string> parameters; FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command)); if (!MOD_RESULT) ServerInstance->Parser.CallHandler(command, parameters, this); MOD_RESULT = MOD_RES_PASSTHRU; command = "MOTD"; FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, parameters, this, true, command)); if (!MOD_RESULT) ServerInstance->Parser.CallHandler(command, parameters, this); if (ServerInstance->Config->RawLog) WriteServ("PRIVMSG %s :*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.", nick.c_str()); /* * We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff * for a user that doesn't exist yet. */ FOREACH_MOD(OnUserConnect, (this)); this->registered = REG_ALL; FOREACH_MOD(OnPostConnect, (this)); ServerInstance->SNO->WriteToSnoMask('c',"Client connecting on port %d (class %s): %s (%s) [%s]", this->GetServerPort(), this->MyClass->name.c_str(), GetFullRealHost().c_str(), this->GetIPString().c_str(), this->fullname.c_str()); ServerInstance->Logs->Log("BANCACHE", LOG_DEBUG, "BanCache: Adding NEGATIVE hit for " + this->GetIPString()); ServerInstance->BanCache.AddHit(this->GetIPString(), "", ""); // reset the flood penalty (which could have been raised due to things like auto +x) CommandFloodPenalty = 0; }
/* * Sets a user's connection class. * If the class name is provided, it will be used. Otherwise, the class will be guessed using host/ip/ident/etc. * NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves, * then their ip will be taken as 'priority' anyway, so for example, * <connect allow="127.0.0.1"> will match joe!bloggs@localhost */ void LocalUser::SetClass(const std::string &explicit_name) { ConnectClass *found = NULL; ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Setting connect class for UID %s", this->uuid.c_str()); if (!explicit_name.empty()) { for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i) { ConnectClass* c = *i; if (explicit_name == c->name) { ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Explicitly set to %s", explicit_name.c_str()); found = c; } } } else { for (ServerConfig::ClassVector::const_iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); ++i) { ConnectClass* c = *i; ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Checking %s", c->GetName().c_str()); ModResult MOD_RESULT; FIRST_MOD_RESULT(OnSetConnectClass, MOD_RESULT, (this,c)); if (MOD_RESULT == MOD_RES_DENY) continue; if (MOD_RESULT == MOD_RES_ALLOW) { ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Class forced by module to %s", c->GetName().c_str()); found = c; break; } if (c->type == CC_NAMED) continue; bool regdone = (registered != REG_NONE); if (c->config->getBool("registered", regdone) != regdone) continue; /* check if host matches.. */ if (!InspIRCd::MatchCIDR(this->GetIPString(), c->GetHost(), NULL) && !InspIRCd::MatchCIDR(this->host, c->GetHost(), NULL)) { ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "No host match (for %s)", c->GetHost().c_str()); continue; } /* * deny change if change will take class over the limit check it HERE, not after we found a matching class, * because we should attempt to find another class if this one doesn't match us. -- w00t */ if (c->limit && (c->GetReferenceCount() >= c->limit)) { ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "OOPS: Connect class limit (%lu) hit, denying", c->limit); continue; } /* if it requires a port ... */ int port = c->config->getInt("port"); if (port) { ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Requires port (%d)", port); /* and our port doesn't match, fail. */ if (this->GetServerPort() != port) continue; } if (regdone && !c->config->getString("password").empty()) { if (!ServerInstance->PassCompare(this, c->config->getString("password"), password, c->config->getString("hash"))) { ServerInstance->Logs->Log("CONNECTCLASS", LOG_DEBUG, "Bad password, skipping"); continue; } } /* we stop at the first class that meets ALL critera. */ found = c; break; } } /* * Okay, assuming we found a class that matches.. switch us into that class, keeping refcounts up to date. */ if (found) { MyClass = found; } }
/** Handle /KILL */ CmdResult CommandKill::Handle (const std::vector<std::string>& parameters, User *user) { /* Allow comma seperated lists of users for /KILL (thanks w00t) */ if (CommandParser::LoopCall(user, this, parameters, 0)) { // If we got a colon delimited list of nicks then the handler ran for each nick, // and KILL commands were broadcast for remote targets. return CMD_FAILURE; } User *u = ServerInstance->FindNick(parameters[0]); if ((u) && (!IS_SERVER(u))) { /* * Here, we need to decide how to munge kill messages. Whether to hide killer, what to show opers, etc. * We only do this when the command is being issued LOCALLY, for remote KILL, we just copy the message we got. * * This conditional is so that we only append the "Killed (" prefix ONCE. If killer is remote, then the kill * just gets processed and passed on, otherwise, if they are local, it gets prefixed. Makes sense :-) -- w00t */ if (IS_LOCAL(user)) { /* * Moved this event inside the IS_LOCAL check also, we don't want half the network killing a user * and the other half not. This would be a bad thing. ;p -- w00t */ ModResult MOD_RESULT; FIRST_MOD_RESULT(OnKill, MOD_RESULT, (user, u, parameters[1])); if (MOD_RESULT == MOD_RES_DENY) return CMD_FAILURE; killreason = "Killed ("; if (!ServerInstance->Config->HideKillsServer.empty()) { // hidekills is on, use it killreason += ServerInstance->Config->HideKillsServer; } else { // hidekills is off, do nothing killreason += user->nick; } killreason += " (" + parameters[1] + "))"; } else { /* Leave it alone, remote server has already formatted it */ killreason.assign(parameters[1], 0, ServerInstance->Config->Limits.MaxQuit); } /* * Now we need to decide whether or not to send a local or remote snotice. Currently this checking is a little flawed. * No time to fix it right now, so left a note. -- w00t */ if (!IS_LOCAL(u)) { // remote kill if (!user->server->IsULine()) ServerInstance->SNO->WriteToSnoMask('K', "Remote kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str()); this->lastuuid = u->uuid; } else { // local kill /* * XXX - this isn't entirely correct, servers A - B - C, oper on A, client on C. Oper kills client, A and B will get remote kill * snotices, C will get a local kill snotice. this isn't accurate, and needs fixing at some stage. -- w00t */ if (!user->server->IsULine()) { if (IS_LOCAL(user)) ServerInstance->SNO->WriteGlobalSno('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str()); else ServerInstance->SNO->WriteToSnoMask('k',"Local Kill by %s: %s (%s)", user->nick.c_str(), u->GetFullRealHost().c_str(), parameters[1].c_str()); } ServerInstance->Logs->Log("KILL", LOG_DEFAULT, "LOCAL KILL: %s :%s!%s!%s (%s)", u->nick.c_str(), ServerInstance->Config->ServerName.c_str(), user->dhost.c_str(), user->nick.c_str(), parameters[1].c_str()); u->Write(":%s KILL %s :%s!%s!%s (%s)", ServerInstance->Config->HideKillsServer.empty() ? user->GetFullHost().c_str() : ServerInstance->Config->HideKillsServer.c_str(), u->nick.c_str(), ServerInstance->Config->ServerName.c_str(), user->dhost.c_str(), ServerInstance->Config->HideKillsServer.empty() ? user->nick.c_str() : ServerInstance->Config->HideKillsServer.c_str(), parameters[1].c_str()); this->lastuuid.clear(); } // send the quit out ServerInstance->Users->QuitUser(u, killreason); } else { user->WriteNumeric(ERR_NOSUCHNICK, "%s :No such nick/channel", parameters[0].c_str()); return CMD_FAILURE; } return CMD_SUCCESS; }
ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, bool adding, const unsigned char modechar, std::string ¶meter, bool SkipACL) { ModeType type = chan ? MODETYPE_CHANNEL : MODETYPE_USER; ModeHandler *mh = FindMode(modechar, type); int pcnt = mh->GetNumParams(adding); // crop mode parameter size to 250 characters if (parameter.length() > 250 && adding) parameter = parameter.substr(0, 250); ModResult MOD_RESULT; FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, modechar, parameter, adding, pcnt)); if (IS_LOCAL(user) && (MOD_RESULT == MOD_RES_DENY)) return MODEACTION_DENY; if (chan && !SkipACL && (MOD_RESULT != MOD_RES_ALLOW)) { MOD_RESULT = mh->AccessCheck(user, chan, parameter, adding); if (MOD_RESULT == MOD_RES_DENY) return MODEACTION_DENY; if (MOD_RESULT == MOD_RES_PASSTHRU) { unsigned int neededrank = mh->GetLevelRequired(); /* Compare our rank on the channel against the rank of the required prefix, * allow if >= ours. Because mIRC and xchat throw a tizz if the modes shown * in NAMES(X) are not in rank order, we know the most powerful mode is listed * first, so we don't need to iterate, we just look up the first instead. */ unsigned int ourrank = chan->GetPrefixValue(user); if (ourrank < neededrank) { PrefixMode* neededmh = NULL; for(char c='A'; c <= 'z'; c++) { PrefixMode* privmh = FindPrefixMode(c); if (privmh && privmh->GetPrefixRank() >= neededrank) { // this mode is sufficient to allow this action if (!neededmh || privmh->GetPrefixRank() < neededmh->GetPrefixRank()) neededmh = privmh; } } if (neededmh) user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must have channel %s access or above to %sset channel mode %c", user->nick.c_str(), chan->name.c_str(), neededmh->name.c_str(), adding ? "" : "un", modechar); else user->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You cannot %sset channel mode %c", user->nick.c_str(), chan->name.c_str(), adding ? "" : "un", modechar); return MODEACTION_DENY; } } } // Ask mode watchers whether this mode change is OK std::pair<ModeWatchIter, ModeWatchIter> itpair = modewatchermap.equal_range(mh->name); for (ModeWatchIter i = itpair.first; i != itpair.second; ++i) { ModeWatcher* mw = i->second; if (mw->GetModeType() == type) { if (!mw->BeforeMode(user, targetuser, chan, parameter, adding)) return MODEACTION_DENY; // A module whacked the parameter completely, and there was one. Abort. if (pcnt && parameter.empty()) return MODEACTION_DENY; } } if (IS_LOCAL(user) && !user->IsOper()) { char* disabled = (type == MODETYPE_CHANNEL) ? ServerInstance->Config->DisabledCModes : ServerInstance->Config->DisabledUModes; if (disabled[modechar - 'A']) { user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - %s mode %c has been locked by the administrator", user->nick.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar); return MODEACTION_DENY; } } if (adding && IS_LOCAL(user) && mh->NeedsOper() && !user->HasModePermission(modechar, type)) { /* It's an oper only mode, and they don't have access to it. */ if (user->IsOper()) { user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Oper type %s does not have access to set %s mode %c", user->nick.c_str(), user->oper->name.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar); } else { user->WriteNumeric(ERR_NOPRIVILEGES, "%s :Permission Denied - Only operators may set %s mode %c", user->nick.c_str(), type == MODETYPE_CHANNEL ? "channel" : "user", modechar); } return MODEACTION_DENY; } /* Call the handler for the mode */ ModeAction ma = mh->OnModeChange(user, targetuser, chan, parameter, adding); if (pcnt && parameter.empty()) return MODEACTION_DENY; if (ma != MODEACTION_ALLOW) return ma; if ((!mh->IsListMode()) && (mh->GetNumParams(true)) && (chan)) chan->SetModeParam(mh, (adding ? parameter : "")); itpair = modewatchermap.equal_range(mh->name); for (ModeWatchIter i = itpair.first; i != itpair.second; ++i) { ModeWatcher* mw = i->second; if (mw->GetModeType() == type) mw->AfterMode(user, targetuser, chan, parameter, adding); } return MODEACTION_ALLOW; }
/* this returns true when all modules are satisfied that the user should be allowed onto the irc server * (until this returns true, a user will block in the waiting state, waiting to connect up to the * registration timeout maximum seconds) */ bool UserManager::AllModulesReportReady(LocalUser* user) { ModResult res; FIRST_MOD_RESULT(OnCheckReady, res, (user)); return (res == MOD_RES_PASSTHRU); }
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; }
void CommandParser::ProcessCommand(LocalUser *user, std::string &cmd) { std::vector<std::string> command_p; irc::tokenstream tokens(cmd); std::string command, token; tokens.GetToken(command); /* A client sent a nick prefix on their command (ick) * rhapsody and some braindead bouncers do this -- * the rfc says they shouldnt but also says the ircd should * discard it if they do. */ if (command[0] == ':') tokens.GetToken(command); while (tokens.GetToken(token)) command_p.push_back(token); std::transform(command.begin(), command.end(), command.begin(), ::toupper); /* find the command, check it exists */ Command* handler = GetHandler(command); /* Modify the user's penalty regardless of whether or not the command exists */ if (!user->HasPrivPermission("users/flood/no-throttle")) { // If it *doesn't* exist, give it a slightly heftier penalty than normal to deter flooding us crap user->CommandFloodPenalty += handler ? handler->Penalty * 1000 : 2000; } if (!handler) { ModResult MOD_RESULT; FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false, cmd)); if (MOD_RESULT == MOD_RES_DENY) return; /* * This double lookup is in case a module (abbreviation) wishes to change a command. * Sure, the double lookup is a bit painful, but bear in mind this only happens for unknowns anyway. * * Thanks dz for making me actually understand why this is necessary! * -- w00t */ handler = GetHandler(command); if (!handler) { if (user->registered == REG_ALL) user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :Unknown command",command.c_str()); ServerInstance->stats.Unknown++; return; } } // If we were given more parameters than max_params then append the excess parameter(s) // to command_p[maxparams-1], i.e. to the last param that is still allowed if (handler->max_params && command_p.size() > handler->max_params) { /* * command_p input (assuming max_params 1): * this * is * a * test */ // Iterator to the last parameter that will be kept const std::vector<std::string>::iterator lastkeep = command_p.begin() + (handler->max_params - 1); // Iterator to the first excess parameter const std::vector<std::string>::iterator firstexcess = lastkeep + 1; // Append all excess parameter(s) to the last parameter, seperated by spaces for (std::vector<std::string>::const_iterator i = firstexcess; i != command_p.end(); ++i) { lastkeep->push_back(' '); lastkeep->append(*i); } // Erase the excess parameter(s) command_p.erase(firstexcess, command_p.end()); } /* * We call OnPreCommand here seperately if the command exists, so the magic above can * truncate to max_params if necessary. -- w00t */ ModResult MOD_RESULT; FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, false, cmd)); if (MOD_RESULT == MOD_RES_DENY) return; /* activity resets the ping pending timer */ user->nping = ServerInstance->Time() + user->MyClass->GetPingTime(); if (handler->flags_needed) { if (!user->IsModeSet(handler->flags_needed)) { user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - You do not have the required operator privileges"); return; } if (!user->HasPermission(command)) { user->WriteNumeric(ERR_NOPRIVILEGES, ":Permission Denied - Oper type %s does not have access to command %s", user->oper->name.c_str(), command.c_str()); return; } } if ((user->registered == REG_ALL) && (!user->IsOper()) && (handler->IsDisabled())) { /* command is disabled! */ if (ServerInstance->Config->DisabledDontExist) { user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :Unknown command", command.c_str()); } else { user->WriteNumeric(ERR_UNKNOWNCOMMAND, "%s :This command has been disabled.", command.c_str()); } ServerInstance->SNO->WriteToSnoMask('a', "%s denied for %s (%s@%s)", command.c_str(), user->nick.c_str(), user->ident.c_str(), user->host.c_str()); return; } if ((!command_p.empty()) && (command_p.back().empty()) && (!handler->allow_empty_last_param)) command_p.pop_back(); if (command_p.size() < handler->min_params) { user->WriteNumeric(ERR_NEEDMOREPARAMS, "%s :Not enough parameters.", command.c_str()); if ((ServerInstance->Config->SyntaxHints) && (user->registered == REG_ALL) && (handler->syntax.length())) user->WriteNumeric(RPL_SYNTAX, ":SYNTAX %s %s", handler->name.c_str(), handler->syntax.c_str()); return; } if ((user->registered != REG_ALL) && (!handler->WorksBeforeReg())) { user->WriteNumeric(ERR_NOTREGISTERED, "%s :You have not registered",command.c_str()); } else { /* passed all checks.. first, do the (ugly) stats counters. */ handler->use_count++; /* module calls too */ FIRST_MOD_RESULT(OnPreCommand, MOD_RESULT, (command, command_p, user, true, cmd)); if (MOD_RESULT == MOD_RES_DENY) return; /* * WARNING: be careful, the user may be deleted soon */ CmdResult result = handler->Handle(command_p, user); FOREACH_MOD(OnPostCommand, (handler, command_p, user, result, cmd)); } }