-/* +------------------------------------+\r * | Inspire Internet Relay Chat Daemon |\r * +------------------------------------+\r *\r * InspIRCd: (C) 2002-2007 InspIRCd Development Team\r * See: http://www.inspircd.org/wiki/index.php/Credits\r *\r * This program is free but copyrighted software; see\r * the file COPYING for details.\r *\r * ---------------------------------------------------\r */\r\r#include "inspircd.h"\r#include "users.h"\r#include "modules.h"\r#include "inspstring.h"\r#include "mode.h"\r\r/* +s (secret) */\r#include "modes/cmode_s.h"\r/* +p (private) */\r#include "modes/cmode_p.h"\r/* +b (bans) */\r#include "modes/cmode_b.h"\r/* +m (moderated) */\r#include "modes/cmode_m.h"\r/* +t (only (half) ops can change topic) */\r#include "modes/cmode_t.h"\r/* +n (no external messages) */\r#include "modes/cmode_n.h"\r/* +i (invite only) */\r#include "modes/cmode_i.h"\r/* +k (keyed channel) */\r#include "modes/cmode_k.h"\r/* +l (channel user limit) */\r#include "modes/cmode_l.h"\r/* +o (channel op) */\r#include "modes/cmode_o.h"\r/* +h (channel halfop) */\r#include "modes/cmode_h.h"\r/* +v (channel voice) */\r#include "modes/cmode_v.h"\r/* +s (server notices) */\r#include "modes/umode_s.h"\r/* +w (see wallops) */\r#include "modes/umode_w.h"\r/* +i (invisible) */\r#include "modes/umode_i.h"\r/* +o (operator) */\r#include "modes/umode_o.h"\r/* +n (notice mask - our implementation of snomasks) */\r#include "modes/umode_n.h"\r\rModeHandler::ModeHandler(InspIRCd* Instance, char modeletter, int parameters_on, int parameters_off, bool listmode, ModeType type, bool operonly, char mprefix)\r : ServerInstance(Instance), mode(modeletter), n_params_on(parameters_on), n_params_off(parameters_off), list(listmode), m_type(type), oper(operonly), prefix(mprefix), count(0)\r{\r}\r\rModeHandler::~ModeHandler()\r{\r}\r\rbool ModeHandler::IsListMode()\r{\r return list;\r}\r\runsigned int ModeHandler::GetPrefixRank()\r{\r return 0;\r}\r\runsigned int ModeHandler::GetCount()\r{\r return 0;\r}\r\rvoid ModeHandler::ChangeCount(int modifier)\r{\r count += modifier;\r}\r\rModeType ModeHandler::GetModeType()\r{\r return m_type;\r}\r\rbool ModeHandler::NeedsOper()\r{\r return oper;\r}\r\rchar ModeHandler::GetPrefix()\r{\r return prefix;\r}\r\rint ModeHandler::GetNumParams(bool adding)\r{\r return adding ? n_params_on : n_params_off;\r}\r\rchar ModeHandler::GetModeChar()\r{\r return mode;\r}\r\rModeAction ModeHandler::OnModeChange(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding)\r{\r return MODEACTION_DENY;\r}\r\rModePair ModeHandler::ModeSet(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter)\r{\r if (dest)\r {\r return std::make_pair(dest->IsModeSet(this->mode), "");\r }\r else\r {\r return std::make_pair(channel->IsModeSet(this->mode), "");\r }\r}\r\rvoid ModeHandler::DisplayList(userrec* user, chanrec* channel)\r{\r}\r\rbool ModeHandler::CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, chanrec* channel)\r{\r return (ours < theirs);\r}\r\rModeWatcher::ModeWatcher(InspIRCd* Instance, char modeletter, ModeType type) : ServerInstance(Instance), mode(modeletter), m_type(type)\r{\r}\r\rModeWatcher::~ModeWatcher()\r{\r}\r\rchar ModeWatcher::GetModeChar()\r{\r return mode;\r}\r\rModeType ModeWatcher::GetModeType()\r{\r return m_type;\r}\r\rbool ModeWatcher::BeforeMode(userrec* source, userrec* dest, chanrec* channel, std::string ¶meter, bool adding, ModeType type)\r{\r return true;\r}\r\rvoid ModeWatcher::AfterMode(userrec* source, userrec* dest, chanrec* channel, const std::string ¶meter, bool adding, ModeType type)\r{\r}\r\ruserrec* ModeParser::SanityChecks(userrec *user,const char *dest,chanrec *chan,int status)\r{\r userrec *d;\r if ((!user) || (!dest) || (!chan) || (!*dest))\r {\r return NULL;\r }\r d = ServerInstance->FindNick(dest);\r if (!d)\r {\r user->WriteServ("401 %s %s :No such nick/channel",user->nick, dest);\r return NULL;\r }\r return d;\r}\r\rconst char* ModeParser::Grant(userrec *d,chanrec *chan,int MASK)\r{\r if (!chan)\r return "";\r\r UCListIter n = d->chans.find(chan);\r if (n != d->chans.end())\r {\r if (n->second & MASK)\r {\r return "";\r }\r n->second = n->second | MASK;\r switch (MASK)\r {\r case UCMODE_OP:\r n->first->AddOppedUser(d);\r break;\r case UCMODE_HOP:\r n->first->AddHalfoppedUser(d);\r break;\r case UCMODE_VOICE:\r n->first->AddVoicedUser(d);\r break;\r }\r return d->nick;\r }\r return "";\r}\r\rconst char* ModeParser::Revoke(userrec *d,chanrec *chan,int MASK)\r{\r if (!chan)\r return "";\r\r UCListIter n = d->chans.find(chan);\r if (n != d->chans.end())\r {\r if ((n->second & MASK) == 0)\r {\r return "";\r }\r n->second ^= MASK;\r switch (MASK)\r {\r case UCMODE_OP:\r n->first->DelOppedUser(d);\r break;\r case UCMODE_HOP:\r n->first->DelHalfoppedUser(d);\r break;\r case UCMODE_VOICE:\r n->first->DelVoicedUser(d);\r break;\r }\r return d->nick;\r }\r return "";\r}\r\rvoid ModeParser::DisplayCurrentModes(userrec *user, userrec* targetuser, chanrec* targetchannel, const char* text)\r{\r if (targetchannel)\r {\r /* Display channel's current mode string */\r user->WriteServ("324 %s %s +%s",user->nick, targetchannel->name, targetchannel->ChanModes(targetchannel->HasUser(user)));\r user->WriteServ("329 %s %s %lu", user->nick, targetchannel->name, (unsigned long)targetchannel->age);\r return;\r }\r else if (targetuser)\r {\r if (targetuser->Visibility && !targetuser->Visibility->VisibleTo(user))\r {\r user->WriteServ("401 %s %s :No such nick/channel",user->nick, text);\r return;\r }\r\r if ((targetuser == user) || (IS_OPER(user)))\r {\r /* Display user's current mode string */\r user->WriteServ("221 %s :+%s",targetuser->nick,targetuser->FormatModes());\r if (IS_OPER(targetuser))\r user->WriteServ("008 %s +%s :Server notice mask", targetuser->nick, targetuser->FormatNoticeMasks());\r return;\r }\r else\r {\r user->WriteServ("502 %s :Can't change mode for other users", user->nick);\r return;\r }\r }\r\r /* No such nick/channel */\r user->WriteServ("401 %s %s :No such nick/channel",user->nick, text);\r return;\r}\r\rvoid ModeParser::Process(const char** parameters, int pcnt, userrec *user, bool servermode)\r{\r std::string target = parameters[0];\r ModeType type = MODETYPE_USER;\r unsigned char mask = 0;\r chanrec* targetchannel = ServerInstance->FindChan(parameters[0]);\r userrec* targetuser = ServerInstance->FindNick(parameters[0]);\r\r LastParse.clear();\r\r /* Special case for displaying the list for listmodes,\r * e.g. MODE #chan b, or MODE #chan +b without a parameter\r */\r if ((targetchannel) && (pcnt == 2))\r {\r const char* mode = parameters[1];\r int nonlistmodes_found = 0;\r bool sent[256];\r\r mask = MASK_CHANNEL;\r\r memset(&sent, 0, 256);\r \r while (mode && *mode)\r {\r unsigned char mletter = *mode;\r\r if (*mode == '+')\r {\r mode++;\r continue;\r }\r\r /* Ensure the user doesnt request the same mode twice,\r * so they cant flood themselves off out of idiocy.\r */\r if (!sent[mletter])\r {\r sent[mletter] = true;\r }\r else\r {\r mode++;\r continue;\r }\r\r ModeHandler *mh = this->FindMode(*mode, MODETYPE_CHANNEL);\r bool display = true;\r\r if ((mh) && (mh->IsListMode()))\r {\r if (ServerInstance->Config->HideModeLists[mletter] && (targetchannel->GetStatus(user) < STATUS_HOP))\r {\r user->WriteServ("482 %s %s :Only half-operators and above may view the +%c list",user->nick, targetchannel->name, *mode++);\r continue;\r }\r\r /** See below for a description of what craq this is :D\r */\r unsigned char handler_id = (*mode - 65) | mask;\r\r for(ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)\r {\r std::string dummyparam;\r \r if (!((*watchers)->BeforeMode(user, NULL, targetchannel, dummyparam, true, MODETYPE_CHANNEL)))\r display = false;\r }\r\r if (display)\r mh->DisplayList(user, targetchannel);\r }\r else\r nonlistmodes_found++;\r\r mode++;\r }\r\r /* We didnt have any modes that were non-list, we can return here */\r if (!nonlistmodes_found)\r return;\r }\r\r if (pcnt == 1)\r {\r this->DisplayCurrentModes(user, targetuser, targetchannel, parameters[0]);\r }\r else if (pcnt > 1)\r {\r if (targetchannel)\r {\r type = MODETYPE_CHANNEL;\r mask = MASK_CHANNEL;\r\r /* Extra security checks on channel modes\r * (e.g. are they a (half)op?\r */\r\r if ((IS_LOCAL(user)) && (targetchannel->GetStatus(user) < STATUS_HOP))\r {\r /* We don't have halfop */\r int MOD_RESULT = 0;\r FOREACH_RESULT(I_OnAccessCheck,OnAccessCheck(user, NULL, targetchannel, AC_GENERAL_MODE));\r if (MOD_RESULT == ACR_DENY)\r return;\r\r if (MOD_RESULT == ACR_DEFAULT)\r {\r /* Are we a uline or is it a servermode? */\r if ((!ServerInstance->ULine(user->server)) && (!servermode))\r {\r /* Not enough permission:\r * NOT a uline and NOT a servermode,\r * OR, NOT halfop or above.\r */\r user->WriteServ("482 %s %s :You're not a channel (half)operator",user->nick, targetchannel->name);\r return;\r }\r }\r }\r }\r else if (targetuser)\r {\r type = MODETYPE_USER;\r mask = MASK_USER;\r if ((user != targetuser) && (!ServerInstance->ULine(user->server)))\r {\r user->WriteServ("502 %s :Can't change mode for other users", user->nick);\r return;\r }\r }\r else\r {\r /* No such nick/channel */\r user->WriteServ("401 %s %s :No such nick/channel",user->nick, parameters[0]);\r return;\r }\r\r std::string mode_sequence = parameters[1];\r std::string parameter;\r std::ostringstream parameter_list;\r std::string output_sequence;\r bool adding = true, state_change = false;\r unsigned char handler_id = 0;\r int parameter_counter = 2; /* Index of first parameter */\r int parameter_count = 0;\r bool last_successful_state_change = false;\r\r /* A mode sequence that doesnt start with + or -. Assume +. - Thanks for the suggestion spike (bug#132) */\r if ((*mode_sequence.begin() != '+') && (*mode_sequence.begin() != '-'))\r mode_sequence.insert(0, "+");\r\r for (std::string::const_iterator letter = mode_sequence.begin(); letter != mode_sequence.end(); letter++)\r {\r unsigned char modechar = *letter;\r\r switch (modechar)\r {\r /* NB:\r * For + and - mode characters, we don't just stick the character into the output sequence.\r * This is because the user may do something dumb, like: +-+ooo or +oo-+. To prevent this\r * appearing in the output sequence, we store a flag which says there was a state change,\r * which is set on any + or -, however, the + or - that we finish on is only appended to\r * the output stream in the event it is followed by a non "+ or -" character, such as o or v.\r */\r case '+':\r /* The following expression prevents: +o+o nick nick, compressing it to +oo nick nick,\r * however, will allow the + if it is the first item in the sequence, regardless.\r */\r if ((!adding) || (!output_sequence.length()))\r state_change = true;\r adding = true;\r if (!output_sequence.length())\r last_successful_state_change = false;\r continue;\r break;\r case '-':\r if ((adding) || (!output_sequence.length()))\r state_change = true;\r adding = false;\r if (!output_sequence.length())\r last_successful_state_change = true;\r continue;\r break;\r default:\r\r /**\r * Watch carefully for the sleight of hand trick.\r * 65 is the ascii value of 'A'. We take this from\r * the char we're looking at to get a number between\r * 1 and 127. We then logic-or it to get the hashed\r * position, dependent on wether its a channel or\r * a user mode. This is a little stranger, but a lot\r * faster, than using a map of pairs.\r */\r handler_id = (modechar - 65) | mask;\r\r if (modehandlers[handler_id])\r {\r bool abort = false;\r\r if (modehandlers[handler_id]->GetModeType() == type)\r {\r if (modehandlers[handler_id]->GetNumParams(adding))\r {\r /* This mode expects a parameter, do we have any parameters left in our list to use? */\r if (parameter_counter < pcnt)\r {\r parameter = parameters[parameter_counter++];\r\r /* Yerk, invalid! */\r if ((parameter.find(':') == 0) || (parameter.rfind(' ') != std::string::npos))\r parameter.clear();\r }\r else\r {\r /* No parameter, continue to the next mode */\r continue;\r }\r\r bool had_parameter = !parameter.empty();\r \r for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)\r {\r if ((*watchers)->BeforeMode(user, targetuser, targetchannel, parameter, adding, type) == false)\r {\r abort = true;\r break;\r }\r /* A module whacked the parameter completely, and there was one. abort. */\r if ((had_parameter) && (parameter.empty()))\r {\r abort = true;\r break;\r }\r }\r\r if (abort)\r continue;\r }\r else\r {\r /* Fix by brain: mode watchers not being called for parameterless modes */\r for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)\r {\r if ((*watchers)->BeforeMode(user, targetuser, targetchannel, parameter, adding, type) == false)\r {\r abort = true;\r break;\r }\r }\r\r if (abort)\r continue;\r }\r\r /* It's an oper only mode, check if theyre an oper. If they arent,\r * eat any parameter that came with the mode, and continue to next\r */\r if ((IS_LOCAL(user)) && (modehandlers[handler_id]->NeedsOper()) && (!IS_OPER(user)))\r {\r user->WriteServ("481 %s :Permission Denied - Only IRC operators may %sset %s mode %c", user->nick,\r adding ? "" : "un", type == MODETYPE_CHANNEL ? "channel" : "user",\r modehandlers[handler_id]->GetModeChar());\r continue;\r }\r\r /* Call the handler for the mode */\r ModeAction ma = modehandlers[handler_id]->OnModeChange(user, targetuser, targetchannel, parameter, adding);\r\r if ((modehandlers[handler_id]->GetNumParams(adding)) && (parameter.empty()))\r {\r /* The handler nuked the parameter and they are supposed to have one.\r * We CANT continue now, even if they actually returned MODEACTION_ALLOW,\r * so we bail to the next mode character.\r */\r continue;\r }\r\r if (ma == MODEACTION_ALLOW)\r {\r /* We're about to output a valid mode letter - was there previously a pending state-change? */\r if (state_change)\r {\r if (adding != last_successful_state_change)\r output_sequence.append(adding ? "+" : "-");\r last_successful_state_change = adding;\r }\r \r /* Add the mode letter */\r output_sequence.push_back(modechar);\r\r /* Is there a valid parameter for this mode? If so add it to the parameter list */\r if ((modehandlers[handler_id]->GetNumParams(adding)) && (!parameter.empty()))\r {\r parameter_list << " " << parameter;\r parameter_count++;\r /* Does this mode have a prefix? */\r if (modehandlers[handler_id]->GetPrefix() && targetchannel)\r {\r userrec* user_to_prefix = ServerInstance->FindNick(parameter);\r if (user_to_prefix)\r targetchannel->SetPrefix(user_to_prefix, modehandlers[handler_id]->GetPrefix(),\r modehandlers[handler_id]->GetPrefixRank(), adding);\r }\r }\r\r /* Call all the AfterMode events in the mode watchers for this mode */\r for (ModeWatchIter watchers = modewatchers[handler_id].begin(); watchers != modewatchers[handler_id].end(); watchers++)\r (*watchers)->AfterMode(user, targetuser, targetchannel, parameter, adding, type);\r\r /* Reset the state change flag */\r state_change = false;\r\r if ((output_sequence.length() + parameter_list.str().length() > 450) || (output_sequence.length() > 100)\r || (parameter_count > MAXMODES))\r {\r /* We cant have a mode sequence this long */\r letter = mode_sequence.end() - 1;\r continue;\r }\r }\r }\r }\r else\r {\r /* No mode handler? Unknown mode character then. */\r user->WriteServ("472 %s %c :is unknown mode char to me",user->nick, modechar);\r }\r break;\r }\r }\r\r /* Was there at least one valid mode in the sequence? */\r if (!output_sequence.empty())\r {\r if (servermode)\r {\r if (type == MODETYPE_CHANNEL)\r {\r targetchannel->WriteChannelWithServ(ServerInstance->Config->ServerName, "MODE %s %s%s", targetchannel->name, output_sequence.c_str(), parameter_list.str().c_str());\r this->LastParse = targetchannel->name;\r }\r else\r {\r targetuser->WriteServ("MODE %s %s%s",targetuser->nick,output_sequence.c_str(), parameter_list.str().c_str());\r this->LastParse = targetuser->nick;\r }\r }\r else\r {\r if (type == MODETYPE_CHANNEL)\r {\r targetchannel->WriteChannel(user,"MODE %s %s%s",targetchannel->name,output_sequence.c_str(),parameter_list.str().c_str());\r FOREACH_MOD(I_OnMode,OnMode(user, targetchannel, TYPE_CHANNEL, output_sequence + parameter_list.str()));\r this->LastParse = targetchannel->name;\r }\r else\r {\r user->WriteTo(targetuser,"MODE %s %s%s",targetuser->nick,output_sequence.c_str(), parameter_list.str().c_str());\r FOREACH_MOD(I_OnMode,OnMode(user, targetuser, TYPE_USER, output_sequence + parameter_list.str()));\r this->LastParse = targetuser->nick;\r }\r }\r\r LastParse.append(" ");\r LastParse.append(output_sequence);\r LastParse.append(parameter_list.str());\r }\r }\r}\r\rconst std::string& ModeParser::GetLastParse()\r{\r return LastParse;\r}\r\rvoid ModeParser::CleanMask(std::string &mask)\r{\r std::string::size_type pos_of_pling = mask.find_first_of('!');\r std::string::size_type pos_of_at = mask.find_first_of('@');\r std::string::size_type pos_of_dot = mask.find_first_of('.');\r std::string::size_type pos_of_colon = mask.find_first_of(':'); /* Because ipv6 addresses are colon delimited */\r\r if ((pos_of_pling == std::string::npos) && (pos_of_at == std::string::npos))\r {\r /* Just a nick, or just a host */\r if ((pos_of_dot == std::string::npos) && (pos_of_colon == std::string::npos))\r {\r /* It has no '.' in it, it must be a nick. */\r mask.append("!*@*");\r }\r else\r {\r /* Got a dot in it? Has to be a host */\r mask = "*!*@" + mask;\r }\r }\r else if ((pos_of_pling == std::string::npos) && (pos_of_at != std::string::npos))\r {\r /* Has an @ but no !, its a user@host */\r mask = "*!" + mask;\r }\r else if ((pos_of_pling != std::string::npos) && (pos_of_at == std::string::npos))\r {\r /* Has a ! but no @, it must be a nick!ident */\r mask.append("@*");\r }\r}\r\rbool ModeParser::AddMode(ModeHandler* mh, unsigned const char modeletter)\r{\r unsigned char mask = 0;\r unsigned char pos = 0;\r\r /* Yes, i know, this might let people declare modes like '_' or '^'.\r * If they do that, thats their problem, and if i ever EVER see an\r * official InspIRCd developer do that, i'll beat them with a paddle!\r */\r if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z') || (mh->GetPrefix() > 126))\r return false;\r\r /* A mode prefix of ',' is not acceptable, it would fuck up server to server.\r * A mode prefix of ':' will fuck up both server to server, and client to server.\r * A mode prefix of '#' will mess up /whois and /privmsg\r */\r if ((mh->GetPrefix() == ',') || (mh->GetPrefix() == ':') || (mh->GetPrefix() == '#'))\r return false;\r\r mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;\r pos = (mh->GetModeChar()-65) | mask;\r\r if (modehandlers[pos])\r return false;\r\r modehandlers[pos] = mh;\r return true;\r}\r\rbool ModeParser::DelMode(ModeHandler* mh)\r{\r unsigned char mask = 0;\r unsigned char pos = 0;\r\r if ((mh->GetModeChar() < 'A') || (mh->GetModeChar() > 'z'))\r return false;\r\r mh->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;\r pos = (mh->GetModeChar()-65) | mask;\r\r if (!modehandlers[pos])\r return false;\r\r switch (mh->GetModeType())\r {\r case MODETYPE_USER:\r for (user_hash::iterator i = ServerInstance->clientlist->begin(); i != ServerInstance->clientlist->end(); i++)\r {\r mh->RemoveMode(i->second);\r }\r break;\r case MODETYPE_CHANNEL:\r for (chan_hash::iterator i = ServerInstance->chanlist->begin(); i != ServerInstance->chanlist->end(); i++)\r {\r mh->RemoveMode(i->second);\r }\r break;\r }\r\r modehandlers[pos] = NULL;\r\r return true;\r}\r\rModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)\r{\r unsigned char mask = 0;\r unsigned char pos = 0;\r\r if ((modeletter < 'A') || (modeletter > 'z'))\r return NULL;\r\r mt == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;\r pos = (modeletter-65) | mask;\r\r return modehandlers[pos];\r}\r\rstd::string ModeParser::UserModeList()\r{\r char modestr[256];\r int pointer = 0;\r\r for (unsigned char mode = 'A'; mode <= 'z'; mode++)\r {\r unsigned char pos = (mode-65) | MASK_USER;\r\r if (modehandlers[pos])\r modestr[pointer++] = mode;\r }\r modestr[pointer++] = 0;\r return modestr;\r}\r\rstd::string ModeParser::ChannelModeList()\r{\r char modestr[256];\r int pointer = 0;\r\r for (unsigned char mode = 'A'; mode <= 'z'; mode++)\r {\r if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h'))\r continue;\r\r unsigned char pos = (mode-65) | MASK_CHANNEL;\r\r if (modehandlers[pos])\r modestr[pointer++] = mode;\r }\r modestr[pointer++] = 0;\r return modestr;\r}\r\rstd::string ModeParser::ParaModeList()\r{\r char modestr[256];\r int pointer = 0;\r\r for (unsigned char mode = 'A'; mode <= 'z'; mode++)\r {\r if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h'))\r continue;\r\r unsigned char pos = (mode-65) | MASK_CHANNEL;\r\r if ((modehandlers[pos]) && (modehandlers[pos]->GetNumParams(true)))\r modestr[pointer++] = mode;\r }\r modestr[pointer++] = 0;\r return modestr;\r}\r\rModeHandler* ModeParser::FindPrefix(unsigned const char pfxletter)\r{\r for (unsigned char mode = 'A'; mode <= 'z'; mode++)\r {\r unsigned char pos = (mode-65) | MASK_CHANNEL;\r\r if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix() == pfxletter))\r {\r return modehandlers[pos];\r }\r }\r return NULL;\r}\r\rstd::string ModeParser::ModeString(userrec* user, chanrec* channel)\r{\r std::string types;\r std::string pars;\r\r if (!channel || !user)\r return "";\r\r for (unsigned char mode = 'A'; mode <= 'z'; mode++)\r {\r unsigned char pos = (mode-65) | MASK_CHANNEL;\r ModeHandler* mh = modehandlers[pos];\r if ((mh) && (mh->GetNumParams(true)) && (mh->GetNumParams(false)))\r {\r ModePair ret;\r ret = mh->ModeSet(NULL, user, channel, user->nick);\r if ((ret.first) && (ret.second == user->nick))\r {\r pars.append(" ");\r pars.append(user->nick);\r types.push_back(mh->GetModeChar());\r }\r }\r }\r\r return types+pars;\r}\r\rstd::string ModeParser::ChanModes()\r{\r std::string type1; /* Listmodes EXCEPT those with a prefix */\r std::string type2; /* Modes that take a param when adding or removing */\r std::string type3; /* Modes that only take a param when adding */\r std::string type4; /* Modes that dont take a param */\r\r for (unsigned char mode = 'A'; mode <= 'z'; mode++)\r {\r if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h'))\r continue;\r\r unsigned char pos = (mode-65) | MASK_CHANNEL;\r /* One parameter when adding */\r if (modehandlers[pos])\r { \r if (modehandlers[pos]->GetNumParams(true))\r {\r if ((modehandlers[pos]->IsListMode()) && (!modehandlers[pos]->GetPrefix()))\r {\r type1 += modehandlers[pos]->GetModeChar();\r }\r else\r {\r /* ... and one parameter when removing */\r if (modehandlers[pos]->GetNumParams(false))\r {\r /* But not a list mode */\r if (!modehandlers[pos]->GetPrefix())\r {\r type2 += modehandlers[pos]->GetModeChar();\r }\r }\r else\r {\r /* No parameters when removing */\r type3 += modehandlers[pos]->GetModeChar();\r }\r }\r }\r else\r {\r type4 += modehandlers[pos]->GetModeChar();\r }\r }\r \r }\r\r return type1 + "," + type2 + "," + type3 + "," + type4;\r}\r\rbool ModeParser::PrefixComparison(prefixtype one, prefixtype two)\r{ \r return one.second > two.second;\r}\r\rstd::string ModeParser::BuildPrefixes()\r{\r std::string mletters;\r std::string mprefixes;\r pfxcontainer pfx;\r std::map<char,char> prefix_to_mode;\r\r for (unsigned char mode = 'A'; mode <= 'z'; mode++)\r {\r if ((!ServerInstance->Config->AllowHalfop) && (mode == 'h'))\r continue;\r\r unsigned char pos = (mode-65) | MASK_CHANNEL;\r\r if ((modehandlers[pos]) && (modehandlers[pos]->GetPrefix()))\r {\r pfx.push_back(std::make_pair<char,unsigned int>(modehandlers[pos]->GetPrefix(), modehandlers[pos]->GetPrefixRank()));\r prefix_to_mode[modehandlers[pos]->GetPrefix()] = modehandlers[pos]->GetModeChar();\r }\r }\r\r sort(pfx.begin(), pfx.end(), ModeParser::PrefixComparison);\r\r for (pfxcontainer::iterator n = pfx.begin(); n != pfx.end(); n++)\r {\r mletters = mletters + n->first;\r mprefixes = mprefixes + prefix_to_mode.find(n->first)->second;\r }\r\r return "(" + mprefixes + ")" + mletters;\r}\r\rbool ModeParser::AddModeWatcher(ModeWatcher* mw)\r{\r unsigned char mask = 0;\r unsigned char pos = 0;\r\r if (!mw)\r return false;\r\r if ((mw->GetModeChar() < 'A') || (mw->GetModeChar() > 'z'))\r return false;\r\r mw->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;\r pos = (mw->GetModeChar()-65) | mask;\r\r modewatchers[pos].push_back(mw);\r\r return true;\r}\r\rbool ModeParser::DelModeWatcher(ModeWatcher* mw)\r{\r unsigned char mask = 0;\r unsigned char pos = 0;\r\r if (!mw)\r return false;\r\r if ((mw->GetModeChar() < 'A') || (mw->GetModeChar() > 'z'))\r return false;\r\r mw->GetModeType() == MODETYPE_USER ? mask = MASK_USER : mask = MASK_CHANNEL;\r pos = (mw->GetModeChar()-65) | mask;\r\r ModeWatchIter a = find(modewatchers[pos].begin(),modewatchers[pos].end(),mw);\r\r if (a == modewatchers[pos].end())\r {\r return false;\r }\r\r modewatchers[pos].erase(a);\r\r return true;\r}\r\r/** This default implementation can remove simple user modes\r */\rvoid ModeHandler::RemoveMode(userrec* user)\r{\r char moderemove[MAXBUF];\r const char* parameters[] = { user->nick, moderemove };\r\r if (user->IsModeSet(this->GetModeChar()))\r {\r sprintf(moderemove,"-%c",this->GetModeChar());\r ServerInstance->Parser->CallHandler("MODE", parameters, 2, user);\r }\r}\r\r/** This default implementation can remove simple channel modes\r * (no parameters)\r */\rvoid ModeHandler::RemoveMode(chanrec* channel)\r{\r char moderemove[MAXBUF];\r const char* parameters[] = { channel->name, moderemove };\r\r if (channel->IsModeSet(this->GetModeChar()))\r {\r userrec* n = new userrec(ServerInstance);\r\r sprintf(moderemove,"-%c",this->GetModeChar());\r n->SetFd(FD_MAGIC_NUMBER);\r\r ServerInstance->SendMode(parameters, 2, n);\r\r delete n;\r }\r}\r\rModeParser::ModeParser(InspIRCd* Instance) : ServerInstance(Instance)\r{\r struct Initializer\r {\r char modechar;\r ModeHandler* handler;\r };\r\r Initializer modes[] = {\r { 's', new ModeChannelSecret(Instance) },\r { 'p', new ModeChannelPrivate(Instance) },\r { 'm', new ModeChannelModerated(Instance) },\r { 't', new ModeChannelTopicOps(Instance) },\r { 'n', new ModeChannelNoExternal(Instance) },\r { 'i', new ModeChannelInviteOnly(Instance) },\r { 'k', new ModeChannelKey(Instance) },\r { 'l', new ModeChannelLimit(Instance) },\r { 'b', new ModeChannelBan(Instance) },\r { 'o', new ModeChannelOp(Instance) },\r { 'h', new ModeChannelHalfOp(Instance) },\r { 'v', new ModeChannelVoice(Instance) },\r { 's', new ModeUserServerNotice(Instance) },\r { 'w', new ModeUserWallops(Instance) },\r { 'i', new ModeUserInvisible(Instance) },\r { 'o', new ModeUserOperator(Instance) },\r { 'n', new ModeUserServerNoticeMask(Instance) },\r { 0, NULL }\r };\r\r /* Clear mode list */\r memset(modehandlers, 0, sizeof(modehandlers));\r memset(modewatchers, 0, sizeof(modewatchers));\r\r /* Last parse string */\r LastParse.clear();\r\r /* Initialise the RFC mode letters */\r for (int index = 0; modes[index].modechar; index++)\r this->AddMode(modes[index].handler, modes[index].modechar);\r}\r
\ No newline at end of file
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2019 Matt Schatz <genius3000@g3k.solutions>
+ * Copyright (C) 2017 B00mX0r <b00mx0r@aureus.pw>
+ * Copyright (C) 2016-2019 Sadie Powell <sadie@witchery.services>
+ * Copyright (C) 2012-2016, 2018 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2012, 2019 Robby <robby@chatbelgie.be>
+ * Copyright (C) 2012 Shawn Smith <ShawnSmith0828@gmail.com>
+ * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ * Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org>
+ * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ * Copyright (C) 2008 Robin Burchell <robin+git@viroteck.net>
+ * Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
+ * Copyright (C) 2006-2008, 2010 Craig Edwards <brain@inspircd.org>
+ *
+ * This file is part of InspIRCd. InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "inspircd.h"
+
+ModeHandler::ModeHandler(Module* Creator, const std::string& Name, char modeletter, ParamSpec Params, ModeType type, Class mclass)
+ : ServiceProvider(Creator, Name, SERVICE_MODE)
+ , modeid(ModeParser::MODEID_MAX)
+ , parameters_taken(Params)
+ , mode(modeletter)
+ , oper(false)
+ , list(false)
+ , m_type(type)
+ , type_id(mclass)
+ , ranktoset(HALFOP_VALUE)
+ , ranktounset(HALFOP_VALUE)
+{
+}
+
+CullResult ModeHandler::cull()
+{
+ if (ServerInstance)
+ ServerInstance->Modes->DelMode(this);
+ return classbase::cull();
+}
+
+ModeHandler::~ModeHandler()
+{
+}
+
+bool ModeHandler::NeedsParam(bool adding) const
+{
+ switch (parameters_taken)
+ {
+ case PARAM_ALWAYS:
+ return true;
+ case PARAM_SETONLY:
+ return adding;
+ case PARAM_NONE:
+ break;
+ }
+ return false;
+}
+
+std::string ModeHandler::GetUserParameter(const User* user) const
+{
+ return "";
+}
+
+ModResult ModeHandler::AccessCheck(User*, Channel*, std::string &, bool)
+{
+ return MOD_RES_PASSTHRU;
+}
+
+ModeAction ModeHandler::OnModeChange(User*, User*, Channel*, std::string&, bool)
+{
+ return MODEACTION_DENY;
+}
+
+void ModeHandler::DisplayList(User*, Channel*)
+{
+}
+
+void ModeHandler::DisplayEmptyList(User*, Channel*)
+{
+}
+
+void ModeHandler::OnParameterMissing(User* user, User* dest, Channel* channel)
+{
+ std::string message = InspIRCd::Format("You must specify a parameter for the %s mode.", name.c_str());
+ if (!syntax.empty())
+ message.append(InspIRCd::Format(" Syntax: %s.", syntax.c_str()));
+
+ if (channel)
+ user->WriteNumeric(Numerics::InvalidModeParameter(channel, this, "*", message));
+ else
+ user->WriteNumeric(Numerics::InvalidModeParameter(dest, this, "*", message));
+}
+
+bool ModeHandler::ResolveModeConflict(std::string& theirs, const std::string& ours, Channel*)
+{
+ return (theirs < ours);
+}
+
+void ModeHandler::RegisterService()
+{
+ ServerInstance->Modes.AddMode(this);
+ ServerInstance->Modules.AddReferent((GetModeType() == MODETYPE_CHANNEL ? "mode/" : "umode/") + name, this);
+}
+
+ModeAction SimpleUserModeHandler::OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
+{
+ /* We're either trying to add a mode we already have or
+ remove a mode we don't have, deny. */
+ if (dest->IsModeSet(this) == adding)
+ return MODEACTION_DENY;
+
+ /* adding will be either true or false, depending on if we
+ are adding or removing the mode, since we already checked
+ to make sure we aren't adding a mode we have or that we
+ aren't removing a mode we don't have, we don't have to do any
+ other checks here to see if it's true or false, just add or
+ remove the mode */
+ dest->SetMode(this, adding);
+
+ return MODEACTION_ALLOW;
+}
+
+
+ModeAction SimpleChannelModeHandler::OnModeChange(User* source, User* dest, Channel* channel, std::string ¶meter, bool adding)
+{
+ /* We're either trying to add a mode we already have or
+ remove a mode we don't have, deny. */
+ if (channel->IsModeSet(this) == adding)
+ return MODEACTION_DENY;
+
+ /* adding will be either true or false, depending on if we
+ are adding or removing the mode, since we already checked
+ to make sure we aren't adding a mode we have or that we
+ aren't removing a mode we don't have, we don't have to do any
+ other checks here to see if it's true or false, just add or
+ remove the mode */
+ channel->SetMode(this, adding);
+
+ return MODEACTION_ALLOW;
+}
+
+ModeWatcher::ModeWatcher(Module* Creator, const std::string& modename, ModeType type)
+ : mode(modename), m_type(type), creator(Creator)
+{
+ ServerInstance->Modes->AddModeWatcher(this);
+}
+
+ModeWatcher::~ModeWatcher()
+{
+ ServerInstance->Modes->DelModeWatcher(this);
+}
+
+bool ModeWatcher::BeforeMode(User*, User*, Channel*, std::string&, bool)
+{
+ return true;
+}
+
+void ModeWatcher::AfterMode(User*, User*, Channel*, const std::string&, bool)
+{
+}
+
+PrefixMode::PrefixMode(Module* Creator, const std::string& Name, char ModeLetter, unsigned int Rank, char PrefixChar)
+ : ModeHandler(Creator, Name, ModeLetter, PARAM_ALWAYS, MODETYPE_CHANNEL, MC_PREFIX)
+ , prefix(PrefixChar)
+ , prefixrank(Rank)
+ , selfremove(true)
+{
+ list = true;
+ syntax = "<nick>";
+}
+
+ModResult PrefixMode::AccessCheck(User* src, Channel*, std::string& value, bool adding)
+{
+ if (!adding && src->nick == value && selfremove)
+ return MOD_RES_ALLOW;
+ return MOD_RES_PASSTHRU;
+}
+
+ModeAction PrefixMode::OnModeChange(User* source, User*, Channel* chan, std::string& parameter, bool adding)
+{
+ User* target;
+ if (IS_LOCAL(source))
+ target = ServerInstance->FindNickOnly(parameter);
+ else
+ target = ServerInstance->FindNick(parameter);
+
+ if (!target)
+ {
+ source->WriteNumeric(Numerics::NoSuchNick(parameter));
+ return MODEACTION_DENY;
+ }
+
+ Membership* memb = chan->GetUser(target);
+ if (!memb)
+ return MODEACTION_DENY;
+
+ parameter = target->nick;
+ return (memb->SetPrefix(this, adding) ? MODEACTION_ALLOW : MODEACTION_DENY);
+}
+
+void PrefixMode::Update(unsigned int rank, unsigned int setrank, unsigned int unsetrank, bool selfrm)
+{
+ prefixrank = rank;
+ ranktoset = setrank;
+ ranktounset = unsetrank;
+ selfremove = selfrm;
+}
+
+ModeAction ParamModeBase::OnModeChange(User* source, User*, Channel* chan, std::string& parameter, bool adding)
+{
+ if (adding)
+ {
+ if (chan->GetModeParameter(this) == parameter)
+ return MODEACTION_DENY;
+
+ if (OnSet(source, chan, parameter) != MODEACTION_ALLOW)
+ return MODEACTION_DENY;
+
+ chan->SetMode(this, true);
+
+ // Handler might have changed the parameter internally
+ parameter.clear();
+ this->GetParameter(chan, parameter);
+ }
+ else
+ {
+ if (!chan->IsModeSet(this))
+ return MODEACTION_DENY;
+ this->OnUnsetInternal(source, chan);
+ chan->SetMode(this, false);
+ }
+ return MODEACTION_ALLOW;
+}
+
+ModeAction ModeParser::TryMode(User* user, User* targetuser, Channel* chan, Modes::Change& mcitem, bool SkipACL)
+{
+ ModeType type = chan ? MODETYPE_CHANNEL : MODETYPE_USER;
+
+ ModeHandler* mh = mcitem.mh;
+ bool adding = mcitem.adding;
+ const bool needs_param = mh->NeedsParam(adding);
+
+ std::string& parameter = mcitem.param;
+ // crop mode parameter size to MODE_PARAM_MAX characters
+ if (parameter.length() > MODE_PARAM_MAX && adding)
+ parameter.erase(MODE_PARAM_MAX);
+
+ ModResult MOD_RESULT;
+ FIRST_MOD_RESULT(OnRawMode, MOD_RESULT, (user, chan, mh, parameter, adding));
+
+ if (IS_LOCAL(user) && (MOD_RESULT == MOD_RES_DENY))
+ return MODEACTION_DENY;
+
+ const char modechar = mh->GetModeChar();
+
+ 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(adding);
+ /* 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)
+ {
+ const PrefixMode* neededmh = NULL;
+ const PrefixModeList& prefixmodes = GetPrefixModes();
+ for (PrefixModeList::const_iterator i = prefixmodes.begin(); i != prefixmodes.end(); ++i)
+ {
+ const PrefixMode* const privmh = *i;
+ if (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, chan->name, InspIRCd::Format("You must have channel %s access or above to %sset channel mode %c",
+ neededmh->name.c_str(), adding ? "" : "un", modechar));
+ else
+ user->WriteNumeric(ERR_CHANOPRIVSNEEDED, chan->name, InspIRCd::Format("You cannot %sset channel mode %c", (adding ? "" : "un"), modechar));
+ return MODEACTION_DENY;
+ }
+ }
+ }
+
+ // Ask mode watchers whether this mode change is OK
+ 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() == type)
+ {
+ if (!mw->BeforeMode(user, targetuser, chan, parameter, adding))
+ return MODEACTION_DENY;
+
+ // A module whacked the parameter completely, and there was one. Abort.
+ if ((needs_param) && (parameter.empty()))
+ return MODEACTION_DENY;
+ }
+ }
+
+ if ((chan || (!chan && adding)) && IS_LOCAL(user) && mh->NeedsOper() && !user->HasModePermission(mh))
+ {
+ /* It's an oper only mode, and they don't have access to it. */
+ if (user->IsOper())
+ {
+ user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Oper type %s does not have access to %sset %s mode %c",
+ user->oper->name.c_str(), adding ? "" : "un", type == MODETYPE_CHANNEL ? "channel" : "user", modechar));
+ }
+ else
+ {
+ user->WriteNumeric(ERR_NOPRIVILEGES, InspIRCd::Format("Permission Denied - Only operators may %sset %s mode %c",
+ adding ? "" : "un", 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 ((needs_param) && (parameter.empty()))
+ return MODEACTION_DENY;
+
+ if (ma != MODEACTION_ALLOW)
+ return ma;
+
+ itpair = modewatchermap.equal_range(mh->name);
+ for (ModeWatcherMap::iterator 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;
+}
+
+void ModeParser::ModeParamsToChangeList(User* user, ModeType type, const std::vector<std::string>& parameters, Modes::ChangeList& changelist, unsigned int beginindex, unsigned int endindex)
+{
+ if (endindex > parameters.size())
+ endindex = parameters.size();
+
+ const std::string& mode_sequence = parameters[beginindex];
+
+ bool adding = true;
+ unsigned int param_at = beginindex+1;
+
+ 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->WriteNumeric(type == MODETYPE_CHANNEL ? ERR_UNKNOWNMODE : ERR_UNKNOWNSNOMASK, modechar, "is an unknown mode character");
+ continue;
+ }
+
+ std::string parameter;
+ if ((mh->NeedsParam(adding)) && (param_at < endindex))
+ parameter = parameters[param_at++];
+
+ changelist.push(mh, adding, parameter);
+ }
+}
+
+static bool IsModeParamValid(User* user, Channel* targetchannel, User* targetuser, const Modes::Change& item)
+{
+ // An empty parameter is never acceptable
+ if (item.param.empty())
+ {
+ item.mh->OnParameterMissing(user, targetuser, targetchannel);
+ return false;
+ }
+
+ // The parameter cannot begin with a ':' character or contain a space
+ if ((item.param[0] == ':') || (item.param.find(' ') != std::string::npos))
+ return false;
+
+ return true;
+}
+
+// Returns true if we should apply a merged mode, false if we should skip it
+static bool ShouldApplyMergedMode(Channel* chan, Modes::Change& item)
+{
+ ModeHandler* mh = item.mh;
+ if ((!chan) || (!chan->IsModeSet(mh)) || (mh->IsListMode()))
+ // Mode not set here or merge is not applicable, apply the incoming mode
+ return true;
+
+ // Mode handler decides
+ std::string ours = chan->GetModeParameter(mh);
+ return mh->ResolveModeConflict(item.param, ours, chan);
+}
+
+void ModeParser::Process(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags)
+{
+ // Call ProcessSingle until the entire list is processed, but at least once to ensure
+ // LastParse and LastChangeList are cleared
+ unsigned int processed = 0;
+ do
+ {
+ unsigned int n = ProcessSingle(user, targetchannel, targetuser, changelist, flags, processed);
+ processed += n;
+ }
+ while (processed < changelist.size());
+}
+
+unsigned int ModeParser::ProcessSingle(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags, unsigned int beginindex)
+{
+ LastChangeList.clear();
+
+ unsigned int modes_processed = 0;
+ Modes::ChangeList::List& list = changelist.getlist();
+ for (Modes::ChangeList::List::iterator i = list.begin()+beginindex; i != list.end(); ++i)
+ {
+ modes_processed++;
+
+ Modes::Change& item = *i;
+ ModeHandler* mh = item.mh;
+
+ // If a mode change has been given for a mode that does not exist then reject
+ // it. This can happen when core_reloadmodule attempts to restore a mode that
+ // no longer exists.
+ if (!mh)
+ continue;
+
+ // If the mode is supposed to have a parameter then we first take a look at item.param
+ // and, if we were asked to, also handle mode merges now
+ if (mh->NeedsParam(item.adding))
+ {
+ // Skip the mode if the parameter does not pass basic validation
+ if (!IsModeParamValid(user, targetchannel, targetuser, item))
+ continue;
+
+ // If this is a merge and we won we don't apply this mode
+ if ((flags & MODE_MERGE) && (!ShouldApplyMergedMode(targetchannel, item)))
+ continue;
+ }
+
+ ModeAction ma = TryMode(user, targetuser, targetchannel, item, (!(flags & MODE_CHECKACCESS)));
+
+ if (ma != MODEACTION_ALLOW)
+ continue;
+
+ LastChangeList.push(mh, item.adding, item.param);
+
+ if (LastChangeList.size() >= ServerInstance->Config->Limits.MaxModes)
+ {
+ /* mode sequence is getting too long */
+ break;
+ }
+ }
+
+ if (!LastChangeList.empty())
+ {
+ ClientProtocol::Events::Mode modeevent(user, targetchannel, targetuser, LastChangeList);
+ if (targetchannel)
+ {
+ targetchannel->Write(modeevent);
+ }
+ else
+ {
+ LocalUser* localtarget = IS_LOCAL(targetuser);
+ if (localtarget)
+ localtarget->Send(modeevent);
+ }
+
+ FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastChangeList, flags));
+ }
+
+ return modes_processed;
+}
+
+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::CleanMask(std::string &mask)
+{
+ std::string::size_type pos_of_pling = mask.find_first_of('!');
+ std::string::size_type pos_of_at = mask.find_first_of('@');
+ std::string::size_type pos_of_dot = mask.find_first_of('.');
+ std::string::size_type pos_of_colons = mask.find("::"); /* Because ipv6 addresses are colon delimited -- double so it treats extban as nick */
+
+ if (mask.length() >= 2 && mask[1] == ':')
+ return; // if it's an extban, don't even try guess how it needs to be formed.
+
+ if ((pos_of_pling == std::string::npos) && (pos_of_at == std::string::npos))
+ {
+ /* Just a nick, or just a host - or clearly ipv6 (starting with :) */
+ if ((pos_of_dot == std::string::npos) && (pos_of_colons == std::string::npos) && mask[0] != ':')
+ {
+ /* It has no '.' in it, it must be a nick. */
+ mask.append("!*@*");
+ }
+ else
+ {
+ /* Got a dot in it? Has to be a host */
+ mask = "*!*@" + mask;
+ }
+ }
+ else if ((pos_of_pling == std::string::npos) && (pos_of_at != std::string::npos))
+ {
+ /* Has an @ but no !, its a user@host */
+ mask = "*!" + mask;
+ }
+ else if ((pos_of_pling != std::string::npos) && (pos_of_at == std::string::npos))
+ {
+ /* Has a ! but no @, it must be a nick!ident */
+ mask.append("@*");
+ }
+}
+
+ModeHandler::Id ModeParser::AllocateModeId(ModeType mt)
+{
+ for (ModeHandler::Id i = 0; i != MODEID_MAX; ++i)
+ {
+ if (!modehandlersbyid[mt][i])
+ return i;
+ }
+
+ throw ModuleException("Out of ModeIds");
+}
+
+void ModeParser::AddMode(ModeHandler* mh)
+{
+ if (!ModeParser::IsModeChar(mh->GetModeChar()))
+ throw ModuleException(InspIRCd::Format("Mode letter for %s is invalid: %c",
+ mh->name.c_str(), mh->GetModeChar()));
+
+ /* A mode prefix of ',' is not acceptable, it would fuck up server to server.
+ * A mode prefix of ':' will fuck up both server to server, and client to server.
+ * A mode prefix of '#' will mess up /whois and /privmsg
+ */
+ PrefixMode* pm = mh->IsPrefixMode();
+ if (pm)
+ {
+ if ((pm->GetPrefix() > 126) || (pm->GetPrefix() == ',') || (pm->GetPrefix() == ':') || (pm->GetPrefix() == '#'))
+ throw ModuleException(InspIRCd::Format("Mode prefix for %s is invalid: %c",
+ mh->name.c_str(), pm->GetPrefix()));
+
+ PrefixMode* otherpm = FindPrefix(pm->GetPrefix());
+ if (otherpm)
+ throw ModuleException(InspIRCd::Format("Mode prefix for %s already used by %s from %s: %c",
+ mh->name.c_str(), otherpm->name.c_str(), otherpm->creator->ModuleSourceFile.c_str(), pm->GetPrefix()));
+ }
+
+ ModeHandler*& slot = modehandlers[mh->GetModeType()][mh->GetModeChar()-65];
+ if (slot)
+ throw ModuleException(InspIRCd::Format("Mode letter for %s already used by %s from %s: %c",
+ mh->name.c_str(), slot->name.c_str(), slot->creator->ModuleSourceFile.c_str(), mh->GetModeChar()));
+
+ // The mode needs an id if it is either a user mode, a simple mode (flag) or a parameter mode.
+ // Otherwise (for listmodes and prefix modes) the id remains MODEID_MAX, which is invalid.
+ ModeHandler::Id modeid = MODEID_MAX;
+ if ((mh->GetModeType() == MODETYPE_USER) || (mh->IsParameterMode()) || (!mh->IsListMode()))
+ modeid = AllocateModeId(mh->GetModeType());
+
+ std::pair<ModeHandlerMap::iterator, bool> res = modehandlersbyname[mh->GetModeType()].insert(std::make_pair(mh->name, mh));
+ if (!res.second)
+ {
+ ModeHandler* othermh = res.first->second;
+ throw ModuleException(InspIRCd::Format("Mode name %s already used by %c from %s",
+ mh->name.c_str(), othermh->GetModeChar(), othermh->creator->ModuleSourceFile.c_str()));
+ }
+
+ // Everything is fine, add the mode
+
+ // If we allocated an id for this mode then save it and put the mode handler into the slot
+ if (modeid != MODEID_MAX)
+ {
+ mh->modeid = modeid;
+ modehandlersbyid[mh->GetModeType()][modeid] = mh;
+ }
+
+ slot = mh;
+ if (pm)
+ mhlist.prefix.push_back(pm);
+ else if (mh->IsListModeBase())
+ mhlist.list.push_back(mh->IsListModeBase());
+}
+
+bool ModeParser::DelMode(ModeHandler* mh)
+{
+ if (!ModeParser::IsModeChar(mh->GetModeChar()))
+ return false;
+
+ ModeHandlerMap& mhmap = modehandlersbyname[mh->GetModeType()];
+ ModeHandlerMap::iterator mhmapit = mhmap.find(mh->name);
+ if ((mhmapit == mhmap.end()) || (mhmapit->second != mh))
+ return false;
+
+ ModeHandler*& slot = modehandlers[mh->GetModeType()][mh->GetModeChar()-65];
+ if (slot != mh)
+ return false;
+
+ /* Note: We can't stack here, as we have modes potentially being removed across many different channels.
+ * To stack here we have to make the algorithm slower. Discuss.
+ */
+ switch (mh->GetModeType())
+ {
+ case MODETYPE_USER:
+ {
+ const user_hash& users = ServerInstance->Users->GetUsers();
+ for (user_hash::const_iterator i = users.begin(); i != users.end(); )
+ {
+ User* user = i->second;
+ ++i;
+ mh->RemoveMode(user);
+ }
+ }
+ break;
+ case MODETYPE_CHANNEL:
+ {
+ const chan_hash& chans = ServerInstance->GetChans();
+ for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); )
+ {
+ // The channel may not be in the hash after RemoveMode(), see m_permchannels
+ Channel* chan = i->second;
+ ++i;
+
+ Modes::ChangeList changelist;
+ mh->RemoveMode(chan, changelist);
+ this->Process(ServerInstance->FakeClient, chan, NULL, changelist, MODE_LOCALONLY);
+ }
+ }
+ break;
+ }
+
+ mhmap.erase(mhmapit);
+ if (mh->GetId() != MODEID_MAX)
+ modehandlersbyid[mh->GetModeType()][mh->GetId()] = NULL;
+ slot = NULL;
+ if (mh->IsPrefixMode())
+ mhlist.prefix.erase(std::find(mhlist.prefix.begin(), mhlist.prefix.end(), mh->IsPrefixMode()));
+ else if (mh->IsListModeBase())
+ mhlist.list.erase(std::find(mhlist.list.begin(), mhlist.list.end(), mh->IsListModeBase()));
+ return true;
+}
+
+ModeHandler* ModeParser::FindMode(const std::string& modename, ModeType mt)
+{
+ ModeHandlerMap& mhmap = modehandlersbyname[mt];
+ ModeHandlerMap::const_iterator it = mhmap.find(modename);
+ if (it != mhmap.end())
+ return it->second;
+
+ return NULL;
+}
+
+ModeHandler* ModeParser::FindMode(unsigned const char modeletter, ModeType mt)
+{
+ if (!ModeParser::IsModeChar(modeletter))
+ return NULL;
+
+ return modehandlers[mt][modeletter-65];
+}
+
+PrefixMode* ModeParser::FindPrefixMode(unsigned char modeletter)
+{
+ ModeHandler* mh = FindMode(modeletter, MODETYPE_CHANNEL);
+ if (!mh)
+ return NULL;
+ return mh->IsPrefixMode();
+}
+
+PrefixMode* ModeParser::FindPrefix(unsigned const char pfxletter)
+{
+ const PrefixModeList& list = GetPrefixModes();
+ for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ PrefixMode* pm = *i;
+ if (pm->GetPrefix() == pfxletter)
+ return pm;
+ }
+ return NULL;
+}
+
+std::string ModeParser::GiveModeList(ModeType mt)
+{
+ std::string type1; /* Listmodes EXCEPT those with a prefix */
+ std::string type2; /* Modes that take a param when adding or removing */
+ std::string type3; /* Modes that only take a param when adding */
+ std::string type4; /* Modes that dont take a param */
+
+ for (unsigned char mode = 'A'; mode <= 'z'; mode++)
+ {
+ ModeHandler* mh = modehandlers[mt][mode-65];
+ /* One parameter when adding */
+ if (mh)
+ {
+ if (mh->NeedsParam(true))
+ {
+ PrefixMode* pm = mh->IsPrefixMode();
+ if ((mh->IsListMode()) && ((!pm) || (pm->GetPrefix() == 0)))
+ {
+ type1 += mh->GetModeChar();
+ }
+ else
+ {
+ /* ... and one parameter when removing */
+ if (mh->NeedsParam(false))
+ {
+ /* But not a list mode */
+ if (!pm)
+ {
+ type2 += mh->GetModeChar();
+ }
+ }
+ else
+ {
+ /* No parameters when removing */
+ type3 += mh->GetModeChar();
+ }
+ }
+ }
+ else
+ {
+ type4 += mh->GetModeChar();
+ }
+ }
+ }
+
+ return type1 + "," + type2 + "," + type3 + "," + type4;
+}
+
+struct PrefixModeSorter
+{
+ bool operator()(PrefixMode* lhs, PrefixMode* rhs)
+ {
+ return lhs->GetPrefixRank() < rhs->GetPrefixRank();
+ }
+};
+
+std::string ModeParser::BuildPrefixes(bool lettersAndModes)
+{
+ std::string mletters;
+ std::string mprefixes;
+ std::vector<PrefixMode*> prefixes;
+
+ const PrefixModeList& list = GetPrefixModes();
+ for (PrefixModeList::const_iterator i = list.begin(); i != list.end(); ++i)
+ {
+ PrefixMode* pm = *i;
+ if (pm->GetPrefix())
+ prefixes.push_back(pm);
+ }
+
+ std::sort(prefixes.begin(), prefixes.end(), PrefixModeSorter());
+ for (std::vector<PrefixMode*>::const_reverse_iterator n = prefixes.rbegin(); n != prefixes.rend(); ++n)
+ {
+ mletters += (*n)->GetPrefix();
+ mprefixes += (*n)->GetModeChar();
+ }
+
+ return lettersAndModes ? "(" + mprefixes + ")" + mletters : mletters;
+}
+
+void ModeParser::AddModeWatcher(ModeWatcher* mw)
+{
+ modewatchermap.insert(std::make_pair(mw->GetModeName(), mw));
+}
+
+bool ModeParser::DelModeWatcher(ModeWatcher* mw)
+{
+ std::pair<ModeWatcherMap::iterator, ModeWatcherMap::iterator> itpair = modewatchermap.equal_range(mw->GetModeName());
+ for (ModeWatcherMap::iterator i = itpair.first; i != itpair.second; ++i)
+ {
+ if (i->second == mw)
+ {
+ modewatchermap.erase(i);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ModeHandler::RemoveMode(User* user)
+{
+ // Remove the mode if it's set on the user
+ if (user->IsModeSet(this->GetModeChar()))
+ {
+ Modes::ChangeList changelist;
+ changelist.push_remove(this);
+ ServerInstance->Modes->Process(ServerInstance->FakeClient, NULL, user, changelist, ModeParser::MODE_LOCALONLY);
+ }
+}
+
+void ModeHandler::RemoveMode(Channel* channel, Modes::ChangeList& changelist)
+{
+ if (channel->IsModeSet(this))
+ {
+ if (this->NeedsParam(false))
+ // Removing this mode requires a parameter
+ changelist.push_remove(this, channel->GetModeParameter(this));
+ else
+ changelist.push_remove(this);
+ }
+}
+
+void PrefixMode::RemoveMode(Channel* chan, Modes::ChangeList& changelist)
+{
+ const Channel::MemberMap& userlist = chan->GetUsers();
+ for (Channel::MemberMap::const_iterator i = userlist.begin(); i != userlist.end(); ++i)
+ {
+ if (i->second->HasMode(this))
+ changelist.push_remove(this, i->first->nick);
+ }
+}
+
+bool ModeParser::IsModeChar(char chr)
+{
+ return ((chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z'));
+}
+
+ModeParser::ModeParser()
+{
+ /* Clear mode handler list */
+ memset(modehandlers, 0, sizeof(modehandlers));
+ memset(modehandlersbyid, 0, sizeof(modehandlersbyid));
+}
+
+ModeParser::~ModeParser()
+{
+}