1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2017 B00mX0r <b00mx0r@aureus.pw>
5  *   Copyright (C) 2013-2016 Attila Molnar <attilamolnar@hush.com>
6  *   Copyright (C) 2013, 2017-2019 Sadie Powell <sadie@witchery.services>
7  *   Copyright (C) 2012, 2019 Robby <robby@chatbelgie.be>
8  *   Copyright (C) 2010 Craig Edwards <brain@inspircd.org>
9  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
10  *
11  * This file is part of InspIRCd.  InspIRCd is free software: you can
12  * redistribute it and/or modify it under the terms of the GNU General Public
13  * License as published by the Free Software Foundation, version 2.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 
25 #include "inspircd.h"
26 
27 enum
28 {
29 	// InspIRCd-specific.
30 	RPL_ENDOFPROPLIST = 960,
31 	RPL_PROPLIST = 961
32 };
33 
DisplayList(LocalUser * user,Channel * channel)34 static void DisplayList(LocalUser* user, Channel* channel)
35 {
36 	Numeric::ParamBuilder<1> numeric(user, RPL_PROPLIST);
37 	numeric.AddStatic(channel->name);
38 
39 	const ModeParser::ModeHandlerMap& mhs = ServerInstance->Modes->GetModes(MODETYPE_CHANNEL);
40 	for (ModeParser::ModeHandlerMap::const_iterator i = mhs.begin(); i != mhs.end(); ++i)
41 	{
42 		ModeHandler* mh = i->second;
43 		if (!channel->IsModeSet(mh))
44 			continue;
45 		numeric.Add("+" + mh->name);
46 		ParamModeBase* pm = mh->IsParameterMode();
47 		if (pm)
48 		{
49 			if ((pm->IsParameterSecret()) && (!channel->HasUser(user)) && (!user->HasPrivPermission("channels/auspex")))
50 				numeric.Add("<" + mh->name + ">");
51 			else
52 				numeric.Add(channel->GetModeParameter(mh));
53 		}
54 	}
55 	numeric.Flush();
56 	user->WriteNumeric(RPL_ENDOFPROPLIST, channel->name, "End of mode list");
57 }
58 
59 class CommandProp : public SplitCommand
60 {
61  public:
CommandProp(Module * parent)62 	CommandProp(Module* parent)
63 		: SplitCommand(parent, "PROP", 1)
64 	{
65 		syntax = "<channel> [[(+|-)]<mode> [<value>]]";
66 	}
67 
HandleLocal(LocalUser * src,const Params & parameters)68 	CmdResult HandleLocal(LocalUser* src, const Params& parameters) CXX11_OVERRIDE
69 	{
70 		Channel* const chan = ServerInstance->FindChan(parameters[0]);
71 		if (!chan)
72 		{
73 			src->WriteNumeric(Numerics::NoSuchChannel(parameters[0]));
74 			return CMD_FAILURE;
75 		}
76 
77 		if (parameters.size() == 1)
78 		{
79 			DisplayList(src, chan);
80 			return CMD_SUCCESS;
81 		}
82 		unsigned int i = 1;
83 		Modes::ChangeList modes;
84 		while (i < parameters.size())
85 		{
86 			std::string prop = parameters[i++];
87 			if (prop.empty())
88 				continue;
89 			bool plus = prop[0] != '-';
90 			if (prop[0] == '+' || prop[0] == '-')
91 				prop.erase(prop.begin());
92 
93 			ModeHandler* mh = ServerInstance->Modes->FindMode(prop, MODETYPE_CHANNEL);
94 			if (mh)
95 			{
96 				if (mh->NeedsParam(plus))
97 				{
98 					if (i != parameters.size())
99 						modes.push(mh, plus, parameters[i++]);
100 				}
101 				else
102 					modes.push(mh, plus);
103 			}
104 		}
105 		ServerInstance->Modes->ProcessSingle(src, chan, NULL, modes, ModeParser::MODE_CHECKACCESS);
106 		return CMD_SUCCESS;
107 	}
108 };
109 
110 class DummyZ : public ModeHandler
111 {
112  public:
DummyZ(Module * parent)113 	DummyZ(Module* parent) : ModeHandler(parent, "namebase", 'Z', PARAM_ALWAYS, MODETYPE_CHANNEL)
114 	{
115 		list = true;
116 	}
117 
118 	// Handle /MODE #chan Z
DisplayList(User * user,Channel * chan)119 	void DisplayList(User* user, Channel* chan) CXX11_OVERRIDE
120 	{
121 		LocalUser* luser = IS_LOCAL(user);
122 		if (luser)
123 			::DisplayList(luser, chan);
124 	}
125 };
126 
127 class ModuleNamedModes : public Module
128 {
129 	CommandProp cmd;
130 	DummyZ dummyZ;
131  public:
ModuleNamedModes()132 	ModuleNamedModes() : cmd(this), dummyZ(this)
133 	{
134 	}
135 
GetVersion()136 	Version GetVersion() CXX11_OVERRIDE
137 	{
138 		return Version("Provides support for adding and removing modes via their long names.", VF_VENDOR);
139 	}
140 
Prioritize()141 	void Prioritize() CXX11_OVERRIDE
142 	{
143 		ServerInstance->Modules->SetPriority(this, I_OnPreMode, PRIORITY_FIRST);
144 	}
145 
OnPreMode(User * source,User * dest,Channel * channel,Modes::ChangeList & modes)146 	ModResult OnPreMode(User* source, User* dest, Channel* channel, Modes::ChangeList& modes) CXX11_OVERRIDE
147 	{
148 		if (!channel)
149 			return MOD_RES_PASSTHRU;
150 
151 		Modes::ChangeList::List& list = modes.getlist();
152 		for (Modes::ChangeList::List::iterator i = list.begin(); i != list.end(); )
153 		{
154 			Modes::Change& curr = *i;
155 			// Replace all namebase (dummyZ) modes being changed with the actual
156 			// mode handler and parameter. The parameter format of the namebase mode is
157 			// <modename>[=<parameter>].
158 			if (curr.mh == &dummyZ)
159 			{
160 				std::string name = curr.param;
161 				std::string value;
162 				std::string::size_type eq = name.find('=');
163 				if (eq != std::string::npos)
164 				{
165 					value.assign(name, eq + 1, std::string::npos);
166 					name.erase(eq);
167 				}
168 
169 				ModeHandler* mh = ServerInstance->Modes->FindMode(name, MODETYPE_CHANNEL);
170 				if (!mh)
171 				{
172 					// Mode handler not found
173 					i = list.erase(i);
174 					continue;
175 				}
176 
177 				curr.param.clear();
178 				if (mh->NeedsParam(curr.adding))
179 				{
180 					if (value.empty())
181 					{
182 						// Mode needs a parameter but there wasn't one
183 						i = list.erase(i);
184 						continue;
185 					}
186 
187 					// Change parameter to the text after the '='
188 					curr.param = value;
189 				}
190 
191 				// Put the actual ModeHandler in place of the namebase handler
192 				curr.mh = mh;
193 			}
194 
195 			++i;
196 		}
197 
198 		return MOD_RES_PASSTHRU;
199 	}
200 };
201 
202 MODULE_INIT(ModuleNamedModes)
203