Exemple #1
0
	void OnBackgroundTimer(time_t curtime) override
	{
		timedbans expired;
		for (timedbans::iterator i = TimedBanList.begin(); i != TimedBanList.end();)
		{
			if (curtime > i->expire)
			{
				expired.push_back(*i);
				i = TimedBanList.erase(i);
			}
			else
				++i;
		}

		for (timedbans::iterator i = expired.begin(); i != expired.end(); i++)
		{
			std::string mask = i->mask;
			Channel* cr = i->chan;
			{
				const std::string expiry = "*** Timed ban on " + cr->name + " expired.";
				// If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
				PrefixMode* mh = ServerInstance->Modes.FindPrefixMode('h');
				char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@';

				ClientProtocol::Messages::Privmsg notice(ClientProtocol::Messages::Privmsg::nocopy, ServerInstance->FakeClient, cr, expiry, MSG_NOTICE);
				cr->Write(ServerInstance->GetRFCEvents().privmsg, notice, pfxchar);
				ServerInstance->PI->SendChannelNotice(cr, pfxchar, expiry);

				Modes::ChangeList setban;
				setban.push_remove(ServerInstance->Modes.FindMode('b', MODETYPE_CHANNEL), mask);
				ServerInstance->Modes.Process(ServerInstance->FakeClient, cr, NULL, setban);
			}
		}
	}
void DataKeeper::DoRestoreChans()
{
	ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restoring channel data");
	Modes::ChangeList modechange;

	for (std::vector<ChanData>::const_iterator i = chandatalist.begin(); i != chandatalist.end(); ++i)
	{
		const ChanData& chandata = *i;
		Channel* const chan = ServerInstance->FindChan(chandata.owner);
		if (!chan)
		{
			ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Channel %s not found", chandata.owner.c_str());
			continue;
		}

		RestoreObj(chandata, chan, MODETYPE_CHANNEL, modechange);
		// Process the mode change before applying any prefix modes
		ServerInstance->Modes.Process(ServerInstance->FakeClient, chan, NULL, modechange, ModeParser::MODE_LOCALONLY);
		modechange.clear();

		// Restore all member data
		RestoreMemberData(chan, chandata.memberdatalist, modechange);
		ServerInstance->Modes.Process(ServerInstance->FakeClient, chan, NULL, modechange, ModeParser::MODE_LOCALONLY);
		modechange.clear();
	}
}
Exemple #3
0
void ModuleSpanningTree::OnMode(User* source, User* u, Channel* c, const Modes::ChangeList& modes, ModeParser::ModeProcessFlag processflags)
{
	if (processflags & ModeParser::MODE_LOCALONLY)
		return;

	if (u)
	{
		if (u->registered != REG_ALL)
			return;

		CmdBuilder params(source, "MODE");
		params.push(u->uuid);
		params.push(ClientProtocol::Messages::Mode::ToModeLetters(modes));
		params.push_raw(Translate::ModeChangeListToParams(modes.getlist()));
		params.Broadcast();
	}
	else
	{
		CmdBuilder params(source, "FMODE");
		params.push(c->name);
		params.push_int(c->age);
		params.push(ClientProtocol::Messages::Mode::ToModeLetters(modes));
		params.push_raw(Translate::ModeChangeListToParams(modes.getlist()));
		params.Broadcast();
	}
}
	void SetOperPrefix(User* user, bool add)
	{
		Modes::ChangeList changelist;
		changelist.push(&opm, add, user->nick);
		for (User::ChanList::iterator v = user->chans.begin(); v != user->chans.end(); v++)
			ServerInstance->Modes->Process(ServerInstance->FakeClient, (*v)->chan, NULL, changelist);
	}
Exemple #5
0
void User::UnOper()
{
	if (!this->IsOper())
		return;

	/*
	 * unset their oper type (what IS_OPER checks).
	 * note, order is important - this must come before modes as -o attempts
	 * to call UnOper. -- w00t
	 */
	oper = NULL;


	/* Remove all oper only modes from the user when the deoper - Bug #466*/
	Modes::ChangeList changelist;
	const ModeParser::ModeHandlerMap& usermodes = ServerInstance->Modes->GetModes(MODETYPE_USER);
	for (ModeParser::ModeHandlerMap::const_iterator i = usermodes.begin(); i != usermodes.end(); ++i)
	{
		ModeHandler* mh = i->second;
		if (mh->NeedsOper())
			changelist.push_remove(mh);
	}

	ServerInstance->Modes->Process(this, NULL, this, changelist);

	// Remove the user from the oper list
	stdalgo::vector::swaperase(ServerInstance->Users->all_opers, this);

	ModeHandler* opermh = ServerInstance->Modes->FindMode('o', MODETYPE_USER);
	this->SetMode(opermh, false);
}
Exemple #6
0
	CmdResult Handle(User* user, const Params& parameters) override
	{
		Channel* channel = ServerInstance->FindChan(parameters[0]);
		if (!channel)
		{
			user->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
			return CMD_FAILURE;
		}
		unsigned int cm = channel->GetPrefixValue(user);
		if (cm < HALFOP_VALUE)
		{
			user->WriteNumeric(ERR_CHANOPRIVSNEEDED, channel->name, "You do not have permission to set bans on this channel");
			return CMD_FAILURE;
		}

		TimedBan T;
		unsigned long duration;
		if (!InspIRCd::Duration(parameters[1], duration))
		{
			user->WriteNotice("Invalid ban time");
			return CMD_FAILURE;
		}
		unsigned long expire = duration + ServerInstance->Time();
		std::string mask = parameters[2];
		bool isextban = ((mask.size() > 2) && (mask[1] == ':'));
		if (!isextban && !InspIRCd::IsValidMask(mask))
			mask.append("!*@*");

		if (IsBanSet(channel, mask))
		{
			user->WriteNotice("Ban already set");
			return CMD_FAILURE;
		}

		Modes::ChangeList setban;
		setban.push_add(ServerInstance->Modes.FindMode('b', MODETYPE_CHANNEL), mask);
		// Pass the user (instead of ServerInstance->FakeClient) to ModeHandler::Process() to
		// make it so that the user sets the mode themselves
		ServerInstance->Modes.Process(user, channel, NULL, setban);
		if (ServerInstance->Modes.GetLastChangeList().empty())
		{
			user->WriteNotice("Invalid ban mask");
			return CMD_FAILURE;
		}

		T.mask = mask;
		T.expire = expire + (IS_REMOTE(user) ? 5 : 0);
		T.chan = channel;
		TimedBanList.push_back(T);

		const std::string addban = user->nick + " added a timed ban on " + mask + " lasting for " + InspIRCd::DurationString(duration) + ".";
		// If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
		PrefixMode* mh = ServerInstance->Modes.FindPrefixMode('h');
		char pfxchar = (mh && mh->name == "halfop") ? mh->GetPrefix() : '@';

		ClientProtocol::Messages::Privmsg notice(ServerInstance->FakeClient, channel, addban, MSG_NOTICE);
		channel->Write(ServerInstance->GetRFCEvents().privmsg, notice, pfxchar);
		ServerInstance->PI->SendChannelNotice(channel, pfxchar, addban);
		return CMD_SUCCESS;
	}
Exemple #7
0
	CmdResult Handle (const std::vector<std::string> &parameters, User *user)
	{
		Channel* channel = ServerInstance->FindChan(parameters[0]);
		if (!channel)
		{
			user->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
			return CMD_FAILURE;
		}
		int cm = channel->GetPrefixValue(user);
		if (cm < HALFOP_VALUE)
		{
			user->WriteNumeric(ERR_CHANOPRIVSNEEDED, channel->name, "You do not have permission to set bans on this channel");
			return CMD_FAILURE;
		}

		TimedBan T;
		unsigned long duration = InspIRCd::Duration(parameters[1]);
		unsigned long expire = duration + ServerInstance->Time();
		if (duration < 1)
		{
			user->WriteNotice("Invalid ban time");
			return CMD_FAILURE;
		}
		std::string mask = parameters[2];
		bool isextban = ((mask.size() > 2) && (mask[1] == ':'));
		if (!isextban && !InspIRCd::IsValidMask(mask))
			mask.append("!*@*");

		if (IsBanSet(channel, mask))
		{
			user->WriteNotice("Ban already set");
			return CMD_FAILURE;
		}

		Modes::ChangeList setban;
		setban.push_add(ServerInstance->Modes->FindMode('b', MODETYPE_CHANNEL), mask);
		// Pass the user (instead of ServerInstance->FakeClient) to ModeHandler::Process() to
		// make it so that the user sets the mode themselves
		ServerInstance->Modes->Process(user, channel, NULL, setban);
		if (ServerInstance->Modes->GetLastParse().empty())
		{
			user->WriteNotice("Invalid ban mask");
			return CMD_FAILURE;
		}

		CUList tmp;
		T.mask = mask;
		T.expire = expire + (IS_REMOTE(user) ? 5 : 0);
		T.chan = channel;
		TimedBanList.push_back(T);

		// If halfop is loaded, send notice to halfops and above, otherwise send to ops and above
		ModeHandler* mh = ServerInstance->Modes->FindMode('h', MODETYPE_CHANNEL);
		char pfxchar = (mh && mh->name == "halfop") ? '%' : '@';

		channel->WriteAllExcept(ServerInstance->FakeClient, true, pfxchar, tmp, "NOTICE %s :%s added a timed ban on %s lasting for %ld seconds.", channel->name.c_str(), user->nick.c_str(), mask.c_str(), duration);
		return CMD_SUCCESS;
	}
Exemple #8
0
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);
	}
}
Exemple #9
0
void ModeHandler::RemoveMode(Channel* channel, Modes::ChangeList& changelist)
{
	if (channel->IsModeSet(this))
	{
		if (this->GetNumParams(false))
			// Removing this mode requires a parameter
			changelist.push_remove(this, channel->GetModeParameter(this));
		else
			changelist.push_remove(this);
	}
}
	void OnPostJoin(Membership* memb)
	{
		if ((!IS_LOCAL(memb->user)) || (!memb->user->IsOper()) || (memb->user->IsModeSet(hideopermode)))
			return;

		if (memb->HasMode(&opm))
			return;

		// The user was force joined and OnUserPreJoin() did not run. Set the operprefix now.
		Modes::ChangeList changelist;
		changelist.push_add(&opm, memb->user->nick);
		ServerInstance->Modes.Process(ServerInstance->FakeClient, memb->chan, NULL, changelist);
	}
Exemple #11
0
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, "%c :is unknown mode char to me", modechar);
			continue;
		}

		std::string parameter;
		if (mh->GetNumParams(adding) && param_at < endindex)
			parameter = parameters[param_at++];

		changelist.push(mh, adding, parameter);
	}
}
void DataKeeper::RestoreModes(const std::vector<InstanceData>& list, ModeType modetype, Modes::ChangeList& modechange)
{
	for (std::vector<InstanceData>::const_iterator i = list.begin(); i != list.end(); ++i)
	{
		const InstanceData& id = *i;
		modechange.push_add(handledmodes[modetype][id.index].mh, id.serialized);
	}
}
Exemple #13
0
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->GetModeChar()))
			changelist.push_remove(this, i->first->nick);
	}
}
void DataKeeper::DoRestoreUsers()
{
	ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Restoring user data");
	Modes::ChangeList modechange;

	for (std::vector<UserData>::const_iterator i = userdatalist.begin(); i != userdatalist.end(); ++i)
	{
		const UserData& userdata = *i;
		User* const user = ServerInstance->FindUUID(userdata.owner);
		if (!user)
		{
			ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "User %s is gone", userdata.owner.c_str());
			continue;
		}

		RestoreObj(userdata, user, MODETYPE_USER, modechange);
		ServerInstance->Modes.Process(ServerInstance->FakeClient, NULL, user, modechange, ModeParser::MODE_LOCALONLY);
		modechange.clear();
	}
}
Exemple #15
0
	CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* user)
	{
		// Make sure the channel name is allowable.
		if (!ServerInstance->IsChannel(parameters[0]))
		{
			user->WriteNotice("*** Invalid characters in channel name or name too long");
			return CMD_FAILURE;
		}

		active = true;
		// override is false because we want OnUserPreJoin to run
		Channel* channel = Channel::JoinUser(user, parameters[0], false);
		active = false;

		if (channel)
		{
			ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used OJOIN to join "+channel->name);

			if (notice)
			{
				channel->WriteChannelWithServ(ServerInstance->Config->ServerName, "NOTICE %s :%s joined on official network business.",
					parameters[0].c_str(), user->nick.c_str());
				ServerInstance->PI->SendChannelNotice(channel, 0, user->nick + " joined on official network business.");
			}
		}
		else
		{
			channel = ServerInstance->FindChan(parameters[0]);
			if (!channel)
				return CMD_FAILURE;

			ServerInstance->SNO->WriteGlobalSno('a', user->nick+" used OJOIN in "+parameters[0]);
			// they're already in the channel
			Modes::ChangeList changelist;
			changelist.push_add(npmh, user->nick);
			if (op)
				changelist.push_add(ServerInstance->Modes->FindMode('o', MODETYPE_CHANNEL), user->nick);
			ServerInstance->Modes->Process(ServerInstance->FakeClient, channel, NULL, changelist);
		}
		return CMD_SUCCESS;
	}
Exemple #16
0
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());
}
	CmdResult HandleLocal(const std::vector<std::string>& parameters, LocalUser* src)
	{
		Channel* const chan = ServerInstance->FindChan(parameters[0]);
		if (!chan)
		{
			src->WriteNumeric(Numerics::NoSuchNick(parameters[0]));
			return CMD_FAILURE;
		}

		if (parameters.size() == 1)
		{
			DisplayList(src, chan);
			return CMD_SUCCESS;
		}
		unsigned int i = 1;
		Modes::ChangeList modes;
		while (i < parameters.size())
		{
			std::string prop = parameters[i++];
			if (prop.empty())
				continue;
			bool plus = prop[0] != '-';
			if (prop[0] == '+' || prop[0] == '-')
				prop.erase(prop.begin());

			ModeHandler* mh = ServerInstance->Modes->FindMode(prop, MODETYPE_CHANNEL);
			if (mh)
			{
				if (mh->NeedsParam(plus))
				{
					if (i != parameters.size())
						modes.push(mh, plus, parameters[i++]);
				}
				else
					modes.push(mh, plus);
			}
		}
		ServerInstance->Modes->ProcessSingle(src, chan, NULL, modes, ModeParser::MODE_CHECKACCESS);
		return CMD_SUCCESS;
	}
Exemple #18
0
	ModResult OnUserPreMessage(User* user, const MessageTarget& target, MessageDetails& details) override
	{
		if (target.type != MessageTarget::TYPE_CHANNEL || !IS_LOCAL(user))
			return MOD_RES_PASSTHRU;

		Channel* chan = target.Get<Channel>();
		ChannelSettings* settings = rm.ext.get(chan);
		if (!settings)
			return MOD_RES_PASSTHRU;

		Membership* memb = chan->GetUser(user);
		if (!memb)
			return MOD_RES_PASSTHRU;

		ModResult res = CheckExemption::Call(exemptionprov, user, chan, "repeat");
		if (res == MOD_RES_ALLOW)
			return MOD_RES_PASSTHRU;

		if (rm.MatchLine(memb, settings, details.text))
		{
			if (settings->Action == ChannelSettings::ACT_BLOCK)
			{
				user->WriteNotice("*** This line is too similar to one of your last lines.");
				return MOD_RES_DENY;
			}

			if (settings->Action == ChannelSettings::ACT_BAN)
			{
				Modes::ChangeList changelist;
				changelist.push_add(ServerInstance->Modes.FindMode('b', MODETYPE_CHANNEL), "*!*@" + user->GetDisplayedHost());
				ServerInstance->Modes.Process(ServerInstance->FakeClient, chan, NULL, changelist);
			}

			memb->chan->KickUser(ServerInstance->FakeClient, user, "Repeat flood");
			return MOD_RES_DENY;
		}
		return MOD_RES_PASSTHRU;
	}
Exemple #19
0
void DataKeeper::DoRestoreUsers()
{
	ServerInstance->Logs.Log(MODNAME, LOG_DEBUG, "Restoring user data");
	Modes::ChangeList modechange;

	for (std::vector<UserData>::const_iterator i = userdatalist.begin(); i != userdatalist.end(); ++i)
	{
		const UserData& userdata = *i;
		User* const user = ServerInstance->FindUUID(userdata.owner);
		if (!user)
		{
			ServerInstance->Logs.Log(MODNAME, LOG_DEBUG, "User %s is gone", userdata.owner.c_str());
			continue;
		}

		// Attempt to restore serializer first, if it fails it's a fatal error and RestoreSerializer() quits them
		if (!RestoreSerializer(userdata.serializerindex, user))
			continue;

		RestoreObj(userdata, user, MODETYPE_USER, modechange);
		ServerInstance->Modes.Process(ServerInstance->FakeClient, NULL, user, modechange, ModeParser::MODE_LOCALONLY);
		modechange.clear();
	}
}
Exemple #20
0
	void ValidateChans()
	{
		Modes::ChangeList removepermchan;

		badchan = true;
		const chan_hash& chans = ServerInstance->GetChans();
		for (chan_hash::const_iterator i = chans.begin(); i != chans.end(); )
		{
			Channel* c = i->second;
			// Move iterator before we begin kicking
			++i;
			if (ServerInstance->IsChannel(c->name))
				continue; // The name of this channel is still valid

			if (c->IsModeSet(permchannelmode) && c->GetUserCounter())
			{
				removepermchan.clear();
				removepermchan.push_remove(*permchannelmode);
				ServerInstance->Modes.Process(ServerInstance->FakeClient, c, NULL, removepermchan);
			}

			Channel::MemberMap& users = c->userlist;
			for (Channel::MemberMap::iterator j = users.begin(); j != users.end(); )
			{
				if (IS_LOCAL(j->first))
				{
					// KickUser invalidates the iterator
					Channel::MemberMap::iterator it = j++;
					c->KickUser(ServerInstance->FakeClient, it, "Channel name no longer valid");
				}
				else
					++j;
			}
		}
		badchan = false;
	}
Exemple #21
0
	CmdResult Handle(User* user, const Params& parameters) override
	{
		ModeHandler* mh;
		Channel* chan = ServerInstance->FindChan(parameters[0]);
		char modeletter = parameters[1][0];

		if (chan == NULL)
		{
			user->WriteNotice("The channel " + parameters[0] + " does not exist.");
			return CMD_FAILURE;
		}

		mh = ServerInstance->Modes.FindMode(modeletter, MODETYPE_CHANNEL);
		if (mh == NULL || parameters[1].size() > 1)
		{
			user->WriteNotice(parameters[1] + " is not a valid channel mode.");
			return CMD_FAILURE;
		}

		if (chan->GetPrefixValue(user) < mh->GetLevelRequired(false))
		{
			user->WriteNotice("You do not have access to unset " + ConvToStr(modeletter) + " on " +  chan->name + ".");
			return CMD_FAILURE;
		}

		std::string pattern = parameters.size() > 2 ? parameters[2] : "*";
		PrefixMode* pm;
		ListModeBase* lm;
		ListModeBase::ModeList* ml;
		Modes::ChangeList changelist;

		if ((pm = mh->IsPrefixMode()))
		{
			// As user prefix modes don't have a GetList() method, let's iterate through the channel's users.
			const Channel::MemberMap& users = chan->GetUsers();
			for (Channel::MemberMap::const_iterator it = users.begin(); it != users.end(); ++it)
			{
				if (!InspIRCd::Match(it->first->nick, pattern))
					continue;
				if (it->second->HasMode(pm) && !((it->first == user) && (pm->GetPrefixRank() > VOICE_VALUE)))
					changelist.push_remove(mh, it->first->nick);
			}
		}
		else if ((lm = mh->IsListModeBase()) && ((ml = lm->GetList(chan)) != NULL))
		{
			for (ListModeBase::ModeList::iterator it = ml->begin(); it != ml->end(); ++it)
			{
				if (!InspIRCd::Match(it->mask, pattern))
					continue;
				changelist.push_remove(mh, it->mask);
			}
		}
		else
		{
			if (chan->IsModeSet(mh))
				changelist.push_remove(mh);
		}

		ServerInstance->Modes.Process(user, chan, NULL, changelist);
		return CMD_SUCCESS;
	}
Exemple #22
0
unsigned int ModeParser::ProcessSingle(User* user, Channel* targetchannel, User* targetuser, Modes::ChangeList& changelist, ModeProcessFlag flags, unsigned int beginindex)
{
	LastParse.clear();
	LastChangeList.clear();

	unsigned int modes_processed = 0;
	std::string output_mode;
	std::string output_parameters;

	char output_pm = '\0'; // current output state, '+' or '-'
	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 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->GetNumParams(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;

		char needed_pm = item.adding ? '+' : '-';
		if (needed_pm != output_pm)
		{
			output_pm = needed_pm;
			output_mode.append(1, output_pm);
		}
		output_mode.push_back(mh->GetModeChar());

		if (!item.param.empty())
		{
			output_parameters.push_back(' ');
			output_parameters.append(item.param);
		}
		LastChangeList.push(mh, item.adding, item.param);

		if ((output_mode.length() + output_parameters.length() > 450)
				|| (output_mode.length() > 100)
				|| (LastChangeList.size() >= ServerInstance->Config->Limits.MaxModes))
		{
			/* mode sequence is getting too long */
			break;
		}
	}

	if (!output_mode.empty())
	{
		LastParse = targetchannel ? targetchannel->name : targetuser->nick;
		LastParse.append(" ");
		LastParse.append(output_mode);
		LastParse.append(output_parameters);

		if (targetchannel)
			targetchannel->WriteChannel(user, "MODE " + LastParse);
		else
			targetuser->WriteFrom(user, "MODE " + LastParse);

		FOREACH_MOD(OnMode, (user, targetuser, targetchannel, LastChangeList, flags, output_mode));
	}

	return modes_processed;
}
Exemple #23
0
/** FJOIN, almost identical to TS6 SJOIN, except for nicklist handling. */
CmdResult CommandFJoin::Handle(User* srcuser, Params& params)
{
	/* 1.1+ FJOIN works as follows:
	 *
	 * Each FJOIN is sent along with a timestamp, and the side with the lowest
	 * timestamp 'wins'. From this point on we will refer to this side as the
	 * winner. The side with the higher timestamp loses, from this point on we
	 * will call this side the loser or losing side. This should be familiar to
	 * anyone who's dealt with dreamforge or TS6 before.
	 *
	 * When two sides of a split heal and this occurs, the following things
	 * will happen:
	 *
	 * If the timestamps are exactly equal, both sides merge their privilages
	 * and users, as in InspIRCd 1.0 and ircd2.8. The channels have not been
	 * re-created during a split, this is safe to do.
	 *
	 * If the timestamps are NOT equal, the losing side removes all of its
	 * modes from the channel, before introducing new users into the channel
	 * which are listed in the FJOIN command's parameters. The losing side then
	 * LOWERS its timestamp value of the channel to match that of the winning
	 * side, and the modes of the users of the winning side are merged in with
	 * the losing side.
	 *
	 * The winning side on the other hand will ignore all user modes from the
	 * losing side, so only its own modes get applied. Life is simple for those
	 * who succeed at internets. :-)
	 *
	 * Outside of netbursts, the winning side also resyncs the losing side if it
	 * detects that the other side recreated the channel.
	 *
	 * Syntax:
	 * :<sid> FJOIN <chan> <TS> <modes> :[<member> [<member> ...]]
	 * The last parameter is a list consisting of zero or more channel members
	 * (permanent channels may have zero users). Each entry on the list is in the
	 * following format:
	 * [[<modes>,]<uuid>[:<membid>]
	 * <modes> is a concatenation of the mode letters the user has on the channel
	 * (e.g.: "ov" if the user is opped and voiced). The order of the mode letters
	 * are not important but if a server ecounters an unknown mode letter, it will
	 * drop the link to avoid desync.
	 *
	 * InspIRCd 2.0 and older required a comma before the uuid even if the user
	 * had no prefix modes on the channel, InspIRCd 3.0 and later does not require
	 * a comma in this case anymore.
	 *
	 * <membid> is a positive integer representing the id of the membership.
	 * If not present (in FJOINs coming from pre-1205 servers), 0 is assumed.
	 *
	 * Forwarding:
	 * FJOIN messages are forwarded with the new TS and modes. Prefix modes of
	 * members on the losing side are not forwarded.
	 * This is required to only have one server on each side of the network who
	 * decides the fate of a channel during a network merge. Otherwise, if the
	 * clock of a server is slightly off it may make a different decision than
	 * the rest of the network and desync.
	 * The prefix modes are always forwarded as-is, or not at all.
	 * One incoming FJOIN may result in more than one FJOIN being generated
	 * and forwarded mainly due to compatibility reasons with non-InspIRCd
	 * servers that don't handle more than 512 char long lines.
	 *
	 * Forwarding examples:
	 * Existing channel #chan with TS 1000, modes +n.
	 * Incoming:  :220 FJOIN #chan 1000 +t :o,220AAAAAB:0
	 * Forwarded: :220 FJOIN #chan 1000 +nt :o,220AAAAAB:0
	 * Merge modes and forward the result. Forward their prefix modes as well.
	 *
	 * Existing channel #chan with TS 1000, modes +nt.
	 * Incoming:  :220 FJOIN #CHAN 2000 +i :ov,220AAAAAB:0 o,220AAAAAC:20
	 * Forwarded: :220 FJOIN #chan 1000 +nt :,220AAAAAB:0 ,220AAAAAC:20
	 * Drop their modes, forward our modes and TS, use our channel name
	 * capitalization. Don't forward prefix modes.
	 *
	 */

	time_t TS = ServerCommand::ExtractTS(params[1]);

	const std::string& channel = params[0];
	Channel* chan = ServerInstance->FindChan(channel);
	bool apply_other_sides_modes = true;
	TreeServer* const sourceserver = TreeServer::Get(srcuser);

	if (!chan)
	{
		chan = new Channel(channel, TS);
	}
	else
	{
		time_t ourTS = chan->age;
		if (TS != ourTS)
		{
			ServerInstance->Logs.Log(MODNAME, LOG_DEBUG, "Merge FJOIN received for %s, ourTS: %lu, TS: %lu, difference: %ld",
				chan->name.c_str(), (unsigned long)ourTS, (unsigned long)TS, (long)(ourTS - TS));
			/* If our TS is less than theirs, we dont accept their modes */
			if (ourTS < TS)
			{
				// If the source server isn't bursting then this FJOIN is the result of them recreating the channel with a higher TS.
				// This happens if the last user on the channel hops and before the PART propagates a user on another server joins. Fix it by doing a resync.
				// Servers behind us won't react this way because the forwarded FJOIN will have the correct TS.
				if (!sourceserver->IsBursting())
				{
					ServerInstance->Logs.Log(MODNAME, LOG_DEBUG, "Server %s recreated channel %s with higher TS, resyncing", sourceserver->GetName().c_str(), chan->name.c_str());
					sourceserver->GetSocket()->SyncChannel(chan);
				}
				apply_other_sides_modes = false;
			}
			else if (ourTS > TS)
			{
				// Our TS is greater than theirs, remove all modes, extensions, etc. from the channel
				LowerTS(chan, TS, channel);

				// XXX: If the channel does not exist in the chan hash at this point, create it so the remote modes can be applied on it.
				// This happens to 0-user permanent channels on the losing side, because those are removed (from the chan hash, then
				// deleted later) as soon as the permchan mode is removed from them.
				if (ServerInstance->FindChan(channel) == NULL)
				{
					chan = new Channel(channel, TS);
				}
			}
		}
	}

	// Apply their channel modes if we have to
	Modes::ChangeList modechangelist;
	if (apply_other_sides_modes)
	{
		ServerInstance->Modes.ModeParamsToChangeList(srcuser, MODETYPE_CHANNEL, params, modechangelist, 2, params.size() - 1);
		ServerInstance->Modes.Process(srcuser, chan, NULL, modechangelist, ModeParser::MODE_LOCALONLY | ModeParser::MODE_MERGE);
		// Reuse for prefix modes
		modechangelist.clear();
	}

	// Build a new FJOIN for forwarding. Put the correct TS in it and the current modes of the channel
	// after applying theirs. If they lost, the prefix modes from their message are not forwarded.
	FwdFJoinBuilder fwdfjoin(chan, sourceserver);

	// Process every member in the message
	irc::spacesepstream users(params.back());
	std::string item;
	Modes::ChangeList* modechangelistptr = (apply_other_sides_modes ? &modechangelist : NULL);
	while (users.GetToken(item))
	{
		ProcessModeUUIDPair(item, sourceserver, chan, modechangelistptr, fwdfjoin);
	}

	fwdfjoin.finalize();
	fwdfjoin.Forward(sourceserver->GetRoute());

	// Set prefix modes on their users if we lost the FJOIN or had equal TS
	if (apply_other_sides_modes)
		ServerInstance->Modes.Process(srcuser, chan, NULL, modechangelist, ModeParser::MODE_LOCALONLY);

	return CMD_SUCCESS;
}