Example #1
0
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);
	}
}