1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
|
/*
* InspIRCd -- Internet Relay Chat Daemon
*
* Copyright (C) 2012-2015, 2018 Attila Molnar <attilamolnar@hush.com>
* Copyright (C) 2012-2013, 2017-2022, 2026 Sadie Powell <sadie@witchery.services>
* Copyright (C) 2012 Robby <robby@chatbelgie.be>
* Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
* Copyright (C) 2008 Thomas Stagner <aquanight@gmail.com>
* Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
* Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
* Copyright (C) 2003, 2006-2009 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/>.
*/
#pragma once
/** Used to indicate the type of a command. */
enum class CmdAccess
: uint8_t
{
/* The command has no special attributes. */
NORMAL = 0,
/** Only server operators can use the command. */
OPERATOR = 1,
/** Only servers can use the command. */
SERVER = 2,
};
/** Used to indicate the result of trying to execute a command. */
enum class CmdResult
: uint8_t
{
/** The command exists and its execution succeeded. */
SUCCESS = 0,
/** The command exists but its execution failed. */
FAILURE = 1,
/* The command does not exist. */
INVALID = 2,
};
/** Translation types for translation of parameters to their network form. */
enum class TranslateType
: uint8_t
{
/** The parameter is raw text and should not be translated. */
TEXT,
/** The parameter is a nickname and should be converted to its UUID. */
NICK,
/** The parameter has a translation handled by EncodeParameter/DecodeParameter */
CUSTOM,
};
/** The type of routes that a message can take. */
enum class RouteType
: uint8_t
{
/** The message is only routed to the local server. */
LOCAL,
/** The message is routed to all servers. */
BROADCAST,
/** The message is routed to all servers using ENCAP. */
OPTIONAL_BROADCAST,
/** The message is routed to a specific remote server. */
UNICAST,
/** The message is routed to a specific remote server using ENCAP. */
OPTIONAL_UNICAST,
/** The message is routed to a specific user, channel, or server mask. */
MESSAGE,
};
/** Describes the routing of an IRC message. */
class RouteDescriptor final
{
public:
/** The target of the message in question. */
const std::string target;
/** The type of route that the message should take. */
const RouteType type;
/** For unicast messages a specific server that the message should be routed to. */
const Server* server;
/** Creates a new route descriptor with the specified route type and target string. */
RouteDescriptor(RouteType rt, const std::string& t)
: target(t)
, type(rt)
, server(nullptr)
{
}
RouteDescriptor(RouteType rt, const Server* s = nullptr)
: type(rt)
, server(s)
{
}
};
/** The message is only routed to the local server. */
#define ROUTE_LOCALONLY (RouteDescriptor(RouteType::LOCAL))
/** The message is routed to all servers. */
#define ROUTE_BROADCAST (RouteDescriptor(RouteType::BROADCAST))
/** The message is routed to all servers using ENCAP. */
#define ROUTE_OPT_BCAST (RouteDescriptor(RouteType::OPTIONAL_BROADCAST))
/** The message is routed to a specific remote server. */
#define ROUTE_UNICAST(dest) (RouteDescriptor(RouteType::UNICAST, dest))
/** The message is routed to a specific remote server using ENCAP. */
#define ROUTE_OPT_UCAST(dest) (RouteDescriptor(RouteType::OPTIONAL_UNICAST, dest))
/** The message is routed to a specific user, channel, or server mask. */
#define ROUTE_MESSAGE(dest) (RouteDescriptor(RouteType::MESSAGE, dest))
/** A structure that defines a command. Every command available
* in InspIRCd must be defined as derived from Command.
*/
class CoreExport CommandBase
: public Service::Provider
{
public:
/** Encapsulates parameters to a command. */
class Params : public std::vector<std::string>
{
private:
/* IRCv3 message tags. */
ClientProtocol::TagMap tags;
public:
/** Initializes a new instance from parameter and tag references.
* @param paramsref Message parameters.
* @param tagsref IRCv3 message tags.
*/
Params(const std::vector<std::string>& paramsref, const ClientProtocol::TagMap& tagsref)
: std::vector<std::string>(paramsref)
, tags(tagsref)
{
}
/** Initializes a new instance from parameter iterators.
* @param first The first element in the parameter array.
* @param last The last element in the parameter array.
*/
template<typename Iterator>
Params(Iterator first, Iterator last)
: std::vector<std::string>(first, last)
{
}
/** Initializes a new empty instance. */
Params() = default;
/** Retrieves the IRCv3 message tags. */
const ClientProtocol::TagMap& GetTags() const { return tags; }
ClientProtocol::TagMap& GetTags() { return tags; }
};
/** Minimum number of parameters command takes
*/
const size_t min_params;
/** Maximum number of parameters command takes.
* This is used by the command parser to join extra parameters into one last param.
* If not set, no munging is done to this command.
*/
const size_t max_params;
/** True if the command allows an empty last parameter.
* When false and the last parameter is empty, it's popped BEFORE
* checking there are enough params, etc. (i.e. the handler won't
* be called if there aren't enough params after popping the empty
* param).
*/
bool allow_empty_last_param = false;
/** Translation type list for possible parameters, used to tokenize
* parameters into UIDs and SIDs etc.
*/
std::vector<TranslateType> translation;
/** Create a new command.
* @param me The module which created this command.
* @param cmd Command name. This must be UPPER CASE.
* @param minpara Minimum parameters required for the command.
* @param maxpara Maximum number of parameters this command may have - extra parameters
* will be tossed into one last space-separated param.
*/
CommandBase(const WeakModulePtr& me, const std::string& cmd, size_t minpara = 0, size_t maxpara = 0);
virtual RouteDescriptor GetRouting(User* user, const CommandBase::Params& parameters);
/** Encode a parameter for server->server transmission.
* Used for parameters for which the translation type is TranslateType::CUSTOM.
* @param parameter The parameter to encode.
* @param index The parameter index (0 == first parameter).
*/
virtual std::string EncodeParameter(const std::string& parameter, size_t index);
};
class CoreExport Command
: public CommandBase
{
private:
/** The maximum number of targets for a multi-target command (e.g. KICK). */
size_t max_targets = 0;
/** The time at which max_targets was last checked. */
time_t max_targets_checked = 0;
protected:
/** Initializes a new instance of the Command class.
* @param me The module which created this instance.
* @param cmd The name of the command.
* @param minpara The minimum number of parameters that the command accepts.
* @param maxpara The maximum number of parameters that the command accepts.
*/
Command(const WeakModulePtr& me, const std::string& cmd, size_t minpara = 0, size_t maxpara = 0);
public:
/** Unregisters this command from the command parser. */
~Command() override;
/** Whether this command accepts multiple targets. */
bool accepts_multiple_targets = false;
/** Who can access this command? */
CmdAccess access_needed = CmdAccess::NORMAL;
/** Whether the command will not be forwarded by the linking module even if it comes via ENCAP. */
bool force_manual_route = false;
/** The number of milliseconds worth of penalty that executing this command gives. */
unsigned int penalty = 1000;
/** The number of times this command has been executed. */
unsigned long use_count = 0;
/** If non-empty then the syntax of the parameter for this command. */
std::vector<std::string> syntax;
/** Whether the command can be issued before registering. */
bool works_before_reg = false;
/** Whether the command is currently being loopcalled. */
bool loopcall = false;
/** Retrieves the maximum number of targets this command can accept. */
size_t GetMaxTargets();
/** Handle the command from a user.
* @param user The user who issued the command.
* @param parameters The parameters for the command.
* @return Returns CmdResult::FAILURE on failure, CmdResult::SUCCESS on success, or CmdResult::INVALID
* if the command was malformed.
*/
virtual CmdResult Handle(User* user, const Params& parameters) = 0;
/** Determines if this command is usable by the specified.
* @param user The user to check.
*/
bool IsUsableBy(User* user) const;
/** @copydoc Service::Provider::RegisterService */
void RegisterService() override;
/** @copydoc Service::Provider::UnregisterService */
void UnregisterService() override;
/** Tells the user they did not specify enough parameters.
* @param user The user who issued the command.
* @param parameters The parameters for the command.
*/
virtual void TellNotEnoughParameters(LocalUser* user, const Params& parameters);
/** Tells the user they need to be fully connected to execute this command.
* @param user The user who issued the command.
* @param parameters The parameters for the command.
*/
virtual void TellNotFullyConnected(LocalUser* user, const Params& parameters);
};
class CoreExport SplitCommand
: public Command
{
protected:
/** Initializes a new instance of the SplitCommand class.
* @param me The module which created this instance.
* @param cmd The name of the command.
* @param minpara The minimum number of parameters that the command accepts.
* @param maxpara The maximum number of parameters that the command accepts.
*/
SplitCommand(const WeakModulePtr& me, const std::string& cmd, size_t minpara = 0, size_t maxpara = 0);
public:
/** @copydoc Command::Handle */
CmdResult Handle(User* user, const Params& parameters) override;
/** Handle the command from a local user.
* @param user The user who issued the command.
* @param parameters The parameters for the command.
* @return Returns CmdResult::FAILURE on failure, CmdResult::SUCCESS on success, or CmdResult::INVALID
* if the command was malformed.
*/
virtual CmdResult HandleLocal(LocalUser* user, const Params& parameters);
/** Handle the command from a remote user.
* @param user The user who issued the command.
* @param parameters The parameters for the command.
* @return Returns CmdResult::FAILURE on failure, CmdResult::SUCCESS on success, or CmdResult::INVALID
* if the command was malformed.
*/
virtual CmdResult HandleRemote(RemoteUser* user, const Params& parameters);
/** Handle the command from a server user.
* @param user The user who issued the command.
* @param parameters The parameters for the command.
* @return Returns CmdResult::FAILURE on failure, CmdResult::SUCCESS on success, or CmdResult::INVALID
* if the command was malformed.
*/
virtual CmdResult HandleServer(FakeUser* user, const Params& parameters);
};
/** This class handles command management and parsing.
* It allows you to add and remove commands from the map,
* call command handlers by name, and chop up comma separated
* parameters into multiple calls.
*/
class CoreExport CommandParser final
{
public:
using CommandMap = insp::casemapped_unordered_map<Command*>;
private:
/** Command list, a hash_map of command names to Command*
*/
CommandMap cmdlist;
public:
/** Get a command name -> Command* map containing all client to server commands
* @return A map of command handlers keyed by command names
*/
const CommandMap& GetCommands() const { return cmdlist; }
/** Calls the handler for a given command.
* @param commandname The command to find. This should be in uppercase.
* @param parameters Parameter list
* @param user The user to call the handler on behalf of
* @param cmd If non-NULL and the command was executed it is set to the command handler,
* otherwise it isn't written to.
* @return This method will return CmdResult::SUCCESS if the command handler was found and called,
* and the command completeld successfully. It will return CmdResult::FAILURE if the command handler was found
* and called, but the command did not complete successfully, and it will return CmdResult::INVALID if the
* command simply did not exist at all or the wrong number of parameters were given, or the user
* was not privileged enough to execute the command.
*/
CmdResult CallHandler(const std::string& commandname, const CommandBase::Params& parameters, User* user, Command** cmd = nullptr);
/** Get the handler function for a command.
* @param commandname The command required. Always use uppercase for this parameter.
* @return a pointer to the command handler, or NULL
*/
Command* GetHandler(const std::string& commandname);
/** LoopCall is used to call a command handler repeatedly based on the contents of a comma separated list.
* There are two ways to call this method, either with one potential list or with two potential lists.
* We need to handle two potential lists for JOIN, because a JOIN may contain two lists of items at once:
* the channel names and their keys as follows:
*
* JOIN \#chan1,\#chan2,\#chan3 key1,,key3
*
* Therefore, we need to deal with both lists concurrently. If there are two lists then the method reads
* them both together until the first runs out of tokens.
* With one list it is much simpler, and is used in NAMES, WHOIS, PRIVMSG etc.
*
* If there is only one list and there are duplicates in it, then the command handler is only called for
* unique items. Entries are compared using "irc comparison".
* If the usemax parameter is true (the default) the function only parses until it reaches
* \<maxtargets> number of targets, to stop abuse via spam.
*
* The OnPostCommand hook is executed for each item after it has been processed by the handler, with the
* original line parameter being empty (to indicate that the command in that form was created by this function).
* This only applies if the user executing the command is local.
*
* If there are two lists and the second list runs out of tokens before the first list then parameters[extra]
* will be an EMPTY string when Handle() is called for the remaining tokens in the first list, even if it is
* in the middle of parameters[]! Moreover, empty tokens in the second list are allowed, and those will also
* result in the appropriate entry being empty in parameters[].
* This is different than what command handlers usually expect; the command parser only allows an empty param
* as the last item in the vector.
*
* @param user The user who sent the command
* @param handler The command handler to call for each parameter in the list
* @param parameters Parameter list as a vector of strings
* @param splithere The first parameter index to split as a comma separated list
* @param extra The second parameter index to split as a comma separated list, or -1 (the default) if there is only one list
* @param usemax True to limit the command to \<maxtargets> targets (default), or false to process all tokens
* @return This function returns true when it identified a list in the given parameter and finished calling the
* command handler for each entry on the list. When this occurs, the caller should return without doing anything,
* otherwise it should continue into its main section of code.
*/
static bool LoopCall(User* user, Command* handler, const CommandBase::Params& parameters, size_t splithere, size_t extra = SIZE_MAX, bool usemax = true);
/** Take a raw input buffer from a recvq, and process it on behalf of a user.
* @param buffer The buffer line to process
* @param user The user to whom this line belongs
*/
void ProcessBuffer(LocalUser* user, const std::string& buffer);
/** Process a command from a user.
* @param user The user to parse the command for.
* @param command The name of the command.
* @param parameters The parameters to the command.
*/
void ProcessCommand(LocalUser* user, std::string& command, CommandBase::Params& parameters);
/** Add a new command to the commands hash
* @param f The new Command to add to the list
* @return True if the command was added
*/
bool AddCommand(Command* f);
/** Removes a command.
*/
void RemoveCommand(Command* x);
};
|