-
-
Notifications
You must be signed in to change notification settings - Fork 71
/
Copy pathm_modeaccess.cpp
403 lines (344 loc) · 10.9 KB
/
m_modeaccess.cpp
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
/* +------------------------------------+
* | Inspire Internet Relay Chat Daemon |
* +------------------------------------+
*
* InspIRCd is copyright (C) 2002-2008 ChatSpike-Dev.
* E-mail:
*
* Written by Craig Edwards, Craig McLure, and others.
* This program is free but copyrighted software; see
* the file COPYING for details.
*
* ---------------------------------------------------
*/
#include <vector>
#include <string>
#include <cstring>
#include <algorithm>
#include "inspircd.h"
#include "channels.h"
#include "modules.h"
/* $ModDesc: Allows fine configuration of what users can set arbitrary channel modes, based on usermodes, channel modes and prefixes. */
/* $ModAuthor: Om */
/* $ModAuthorMail: [email protected] */
/* $ModDepends: core 1.1 */
/* $ModVersion: $Rev: 78 $ */
typedef std::pair<char, bool> CharPair;
typedef std::vector<CharPair> CharPairList;
bool cmp(CharPair a, CharPair b)
{
return a.second > b.second;
}
class OnOff
{
private:
void MakeStrRep()
{
bool doneplus;
bool doneminus;
doneplus = doneminus = false;
for(CharPairList::iterator i = modes.begin(); i != modes.end(); i++)
{
if((i->second == 1) && !doneplus)
{
doneplus = true;
strrep.append("+");
}
if((i->second == 0) && !doneminus)
{
doneminus = true;
strrep.append("-");
}
strrep.append(1, i->first);
}
}
public:
std::string strrep;
CharPairList modes;
short modecount;
OnOff(const std::string &combined)
: modecount(0)
{
bool adding = true;
for(std::string::const_iterator i = combined.begin(); i != combined.end(); i++)
{
switch(*i)
{
case '+':
adding = true;
break;
case '-':
adding = false;
break;
default:
modes.push_back(std::make_pair(*i, adding));
modecount++;
break;
}
}
sort(modes.begin(), modes.end(), cmp);
MakeStrRep();
}
OnOff(const std::string &on, const std::string &off)
: modecount(0)
{
for(std::string::const_iterator i = on.begin(); i != on.end(); i++)
{
modes.push_back(std::make_pair(*i, 1));
modecount++;
}
for(std::string::const_iterator i = off.begin(); i != off.end(); i++)
{
modes.push_back(std::make_pair(*i, 0));
modecount++;
}
MakeStrRep();
}
const char* str()
{
return strrep.c_str();
}
};
class Prefix
{
public:
InspIRCd* Srv;
std::string prefixstr;
unsigned int lowestrequired;
char lowestprefixrequired;
Prefix(InspIRCd* me, const std::string &pstr)
: Srv(me), prefixstr(pstr), lowestrequired(0), lowestprefixrequired(' ')
{
bool haveparsedrealprefix = false;
/* First find what the lowest prefix level required is */
for(std::string::const_iterator i = pstr.begin(); i != pstr.end(); i++)
{
ModeHandler* handler = Srv->Modes->FindPrefix(*i);
if(handler)
{
unsigned int newlevel = handler->GetPrefixRank();
if(!haveparsedrealprefix || (newlevel < lowestrequired))
{
lowestrequired = newlevel;
lowestprefixrequired = *i;
}
}
}
}
/** bool UserQualifies(user, channel)
* Return true if the user has the prefixes stored in this class on the channel
* passed as an argument, false otherwise.
*/
bool UserQualifies(User* user, Channel* chan)
{
if(chan->GetPrefixValue(user) >= lowestrequired)
{
return true;
}
else
{
return false;
}
}
};
class PermInfo
{
public:
char mode;
OnOff umodes;
OnOff cmodes;
Prefix prefix;
PermInfo(InspIRCd* me, char m, const std::string &u, const std::string &c, const std::string &p) : mode(m), umodes(u), cmodes(c), prefix(me, p) { }
};
typedef std::vector<PermInfo> PermSet;
typedef std::vector<PermSet> PermissionList;
class ModuleModeAccess : public Module
{
private:
InspIRCd *Srv;
PermissionList perms;
public:
ModuleModeAccess(InspIRCd* Me)
: Module::Module(Me), Srv(Me)
{
OnRehash(NULL, "");
Implementation list[] = { I_On005Numeric, I_OnRawMode, I_OnRehash };
Me->Modules->Attach(list, this, 3);
}
/**
* Load the configuration for the priveleges required to use a certain mode.
* If a more than one tag is defined for a mode, then the user only needs
* to have *one* of those priveleges.
* However if more than one privelege is defined inside one tag, all those
* requirements much be met.
* Prefixes are considered as levels, so if a user has ~ they are considered
* to have &@%+ as well etc.
* If no requirements are set for a mode it is allowed for prefix % and above (default behaviour) Ex:
* <access modes="P" cmodes="s"> - +P may only be set on channels which are +s
* <access modes="c" umodes="h"> - Only users with usermode +h set can set +c
* <access modes="c" umodes="+h"> - Same as above
* <access modes="c" umodes="-h"> - Only users without usermode +h set can set +c
* <access modes="c" prefix="@"> - Only users with prefix @ on a channel can set +c on that channel
* <access modes="S" prefix="~" umodes="o"> - Only opers who are have the ~ prefix on a channel can set +S on the channel
* <access modes="A" cmodes="z"><access modes="A" umodes="z"> - +A can be set by a user with umode +z, or by anyone if the channel has cmode +z
*/
virtual void OnRehash(User* user, const std::string ¶meter)
{
ConfigReader Conf(Srv);
perms.clear();
perms.assign(127, PermSet());
for(int i = 0; i < Conf.Enumerate("access"); i++)
{
std::string rmodes = Conf.ReadValue("access", "modes", i);
std::string umodes = Conf.ReadValue("access", "umodes", i);
std::string cmodes = Conf.ReadValue("access", "cmodes", i);
std::string prefix = Conf.ReadValue("access", "prefix", i);
for(std::string::iterator j = rmodes.begin(); j != rmodes.end(); j++)
{
/* Add a structure for permissions for each mode in the tag. */
perms[ *j ].push_back(PermInfo(Srv, *j, umodes, cmodes, prefix));
Srv->Log(DEBUG, "m_modeaccess.so: Adding mode %c, requires umodes '%s', prefix '%s', and cmodes '%s'", *j, umodes.c_str(), prefix.c_str(), cmodes.c_str());
}
}
}
virtual void On005Numeric(std::string &output)
{
/* Have fun designing some ludicrously complicated format here later... */
/* ALLOWEDMODES=MODE:UMODES:CMODES:PREFIXES */
/* Make a copy of this. Then we load in the modes from the core/other modules
* into the copy and let the code below generate the 005. */
PermissionList tempperms = perms;
std::string umodes = Srv->Modes->ChannelModeList();
for(std::string::iterator i = umodes.begin(); i != umodes.end(); i++)
{
char& mchar = *i;
if((mchar != ',') && (tempperms[mchar].empty()))
{
ModeHandler* handler = Srv->Modes->FindMode(mchar, MODETYPE_CHANNEL);
if(handler)
{
tempperms[mchar].push_back(PermInfo(Srv, mchar, handler->NeedsOper() ? "+o" : "", "", std::string(1, handler->GetNeededPrefix())));
}
}
}
std::string ourstring(" ALLOWEDMODES=");
bool addedamode = false;
for(PermissionList::iterator i = tempperms.begin(); i != tempperms.end(); i++)
{
PermSet& modeperms = *i;
for(PermSet::iterator k = modeperms.begin(); k != modeperms.end(); k++)
{
PermInfo& pinfo = *k;
ourstring.append(1, pinfo.mode).append(1, ':').append(pinfo.umodes.strrep).append(1, ':');
ourstring.append(pinfo.cmodes.strrep).append(1, ':').append(pinfo.prefix.prefixstr).append(1, ',');
addedamode = true;
}
}
if(addedamode)
{
ourstring.resize(ourstring.size() - 1);
output.append(ourstring);
}
}
virtual int OnRawMode(User* source, Channel* channel, const char modechar, const std::string ¶m, bool adding, int pcnt)
{
/* We're only concerned with channel modes */
if(!channel)
return ACR_DEFAULT;
Srv->Log(DEBUG, "OnRawMode(%s, %s, %c, %s, %s, %d)", source->nick, channel->name, modechar, param.c_str(), adding ? "true" : "false", pcnt);
/* We don't care about remote users, this module on the remote server handles them */
if(!IS_LOCAL(source))
return ACR_DEFAULT;
PermSet& criteria = perms[modechar];
char error[MAXBUF];
*error = '\0';
/* Search the rules for this mode, now we iterate over them until we find one that matches */
for(PermSet::iterator iter = criteria.begin(); iter != criteria.end(); iter++)
{
Srv->Log(DEBUG, "Checking over rules for mode %c, possible: umodes='%s', prefix='%s', cmodes='%s'", modechar, iter->umodes.str(), iter->prefix.prefixstr.c_str(), iter->cmodes.str());
/* If the user trying to set the mode matches the criteria for setting it, allow it by returning true */
if(this->HasModes(channel, iter->cmodes))
{
if(this->HasModes(source, iter->umodes))
{
if(iter->prefix.UserQualifies(source, channel))
{
return ACR_ALLOW;
}
else
{
snprintf(error, MAXBUF, "482 %s %s :You must have channel privilege %c or above to %sset channel mode %c", source->nick, channel->name, iter->prefix.lowestprefixrequired, adding ? "" : "un", modechar);
}
}
else
{
snprintf(error, MAXBUF, "482 %s %s :You must have user mode%s %s to %sset channel mode %c", source->nick, channel->name, (iter->umodes.modecount > 1) ? "s" : "", iter->umodes.str(), adding ? "" : "un", modechar);
}
}
else
{
snprintf(error, MAXBUF, "482 %s %s :Channel mode%s %s must to set to %sset channel mode %c", source->nick, channel->name, (iter->cmodes.modecount > 1) ? "s" : "", iter->cmodes.str(), adding ? "" : "un", modechar);
}
}
if(*error)
{
source->WriteServ(error);
return ACR_DENY;
}
else
{
return ACR_DEFAULT;
}
}
/** HasModes(user, modes)
* Return true if user has all the usermodes in 'modes' set.
* Return false otherwise.
*/
bool HasModes(User* user, const OnOff &modes)
{
for(CharPairList::const_iterator i = modes.modes.begin(); i != modes.modes.end(); i++)
{
if(user->IsModeSet(i->first) == i->second)
{
Srv->Log(DEBUG, "Mode %c was %s on %s as configured", i->first, i->second ? "set" : "not set", user->nick);
}
else
{
Srv->Log(DEBUG, "Mode %c was %s on %s, not allowed by configuration", i->first, i->second ? "set" : "not set", user->nick);
return false;
}
}
return true;
}
/** HasModes(chan, modes)
* Return true if all the chanmodes in 'modes' are set on 'chan'
* Return false otherwise.
*/
bool HasModes(Channel* chan, const OnOff &modes)
{
for(CharPairList::const_iterator i = modes.modes.begin(); i != modes.modes.end(); i++)
{
if(chan->IsModeSet(i->first) == i->second)
{
Srv->Log(DEBUG, "Mode %c was %s on %s as configured", i->first, i->second ? "set" : "not set", chan->name);
}
else
{
Srv->Log(DEBUG, "Mode %s was %s on %s, not allowed by configuration", i->first, i->second ? "set" : "not set", chan->name);
return false;
}
}
return true;
}
virtual ~ModuleModeAccess()
{
}
virtual Version GetVersion()
{
return Version(1,2,0,3,0,API_VERSION);
}
};
MODULE_INIT(ModuleModeAccess)