1 |
/** |
2 |
* template.C: This file contains code for the ChanServ TEMPLATE functions. |
3 |
* |
4 |
* Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team |
5 |
* Rights to this code are as documented in COPYING. |
6 |
* |
7 |
* |
8 |
* Portions of this file were derived from sources bearing the following license: |
9 |
* Copyright © 2005-2006 Jilles Tjoelker, et al. |
10 |
* Rights to this code are as documented in doc/pod/license.pod. |
11 |
* |
12 |
* $Id: template.C,v 1.8 2007-09-16 18:54:43 pippijn Exp $ |
13 |
*/ |
14 |
|
15 |
#include "atheme.h" |
16 |
#include <ermyth/module.h> |
17 |
#include <account/myuser.h> |
18 |
#include <account/mychan.h> |
19 |
#include <account/chanacs.h> |
20 |
#include "template.h" |
21 |
|
22 |
static char const rcsid[] = "$Id: template.C,v 1.8 2007-09-16 18:54:43 pippijn Exp $"; |
23 |
|
24 |
REGISTER_MODULE ("chanserv/template", false, "The Ermyth Team <http://ermyth.xinutec.org>"); |
25 |
|
26 |
static void list_generic_flags (sourceinfo_t *si); |
27 |
|
28 |
static void cs_cmd_template (sourceinfo_t *si, int parc, char *parv[]); |
29 |
|
30 |
command_t const cs_flags = { "TEMPLATE", N_("Manipulates predefined sets of flags."), AC_NONE, 3, cs_cmd_template }; |
31 |
|
32 |
E cmdvec cs_cmdtree; |
33 |
E helpvec cs_helptree; |
34 |
|
35 |
bool |
36 |
_modinit (module *m) |
37 |
{ |
38 |
cs_cmdtree << cs_flags; |
39 |
help_addentry (cs_helptree, "TEMPLATE", "help/chanserv/template", NULL); |
40 |
|
41 |
return true; |
42 |
} |
43 |
|
44 |
void |
45 |
_moddeinit () |
46 |
{ |
47 |
cs_cmdtree >> cs_flags; |
48 |
help_delentry (cs_helptree, "TEMPLATE"); |
49 |
} |
50 |
|
51 |
static void |
52 |
list_generic_flags (sourceinfo_t *si) |
53 |
{ |
54 |
command_success_nodata (si, "%-20s %s", _("Name"), _("Flags")); |
55 |
command_success_nodata (si, "%-20s %s", "--------------------", "-----"); |
56 |
command_success_nodata (si, "%-20s %s", "SOP", bitmask_to_flags (chansvs.ca_sop, chanacs_flags)); |
57 |
command_success_nodata (si, "%-20s %s", "AOP", bitmask_to_flags (chansvs.ca_aop, chanacs_flags)); |
58 |
if (chansvs.ca_hop != chansvs.ca_vop) |
59 |
command_success_nodata (si, "%-20s %s", "HOP", bitmask_to_flags (chansvs.ca_hop, chanacs_flags)); |
60 |
command_success_nodata (si, "%-20s %s", "VOP", bitmask_to_flags (chansvs.ca_vop, chanacs_flags)); |
61 |
command_success_nodata (si, "%-20s %s", "--------------------", "-----"); |
62 |
command_success_nodata (si, _("End of network wide template list.")); |
63 |
} |
64 |
|
65 |
/* TEMPLATE [channel] [template] [flags] */ |
66 |
static void |
67 |
cs_cmd_template (sourceinfo_t *si, int parc, char *parv[]) |
68 |
{ |
69 |
metadata *md; |
70 |
int operoverride = 0, changechanacs = 0, l; |
71 |
char *channel = parv[0]; |
72 |
char *target = parv[1]; |
73 |
mychan_t *mc = mychan_t::find (channel); |
74 |
unsigned int oldflags, newflags = 0, addflags, removeflags, restrictflags; |
75 |
char *p, *q, *r; |
76 |
char ss[40], newstr[400]; |
77 |
bool found, denied; |
78 |
|
79 |
if (!channel) |
80 |
{ |
81 |
list_generic_flags (si); |
82 |
logcommand (si, CMDLOG_GET, "TEMPLATE"); |
83 |
return; |
84 |
} |
85 |
|
86 |
mc = mychan_t::find (channel); |
87 |
if (!mc) |
88 |
{ |
89 |
command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), channel); |
90 |
return; |
91 |
} |
92 |
|
93 |
if (!target) |
94 |
{ |
95 |
if (!chanacs_source_has_flag (mc, si, CA_ACLVIEW)) |
96 |
{ |
97 |
if (has_priv (si, PRIV_CHAN_AUSPEX)) |
98 |
operoverride = 1; |
99 |
else |
100 |
{ |
101 |
command_fail (si, fault::noprivs, _("You are not authorized to perform this operation.")); |
102 |
return; |
103 |
} |
104 |
} |
105 |
|
106 |
if (mc->find_metadata ("private:close:closer") && !has_priv (si, PRIV_CHAN_AUSPEX)) |
107 |
{ |
108 |
command_fail (si, fault::noprivs, _("\2%s\2 is closed."), channel); |
109 |
return; |
110 |
} |
111 |
|
112 |
md = mc->find_metadata ("private:templates"); |
113 |
|
114 |
if (md != NULL) |
115 |
{ |
116 |
command_success_nodata (si, "%-20s %s", _("Name"), _("Flags")); |
117 |
command_success_nodata (si, "%-20s %s", "--------------------", "-----"); |
118 |
|
119 |
p = md->value; |
120 |
while (p != NULL) |
121 |
{ |
122 |
while (*p == ' ') |
123 |
p++; |
124 |
q = strchr (p, '='); |
125 |
if (q == NULL) |
126 |
break; |
127 |
r = strchr (q, ' '); |
128 |
command_success_nodata (si, "%-20.*s %.*s", (q - p), p, r != NULL ? (r - q - 1) : (int) strlen (q + 1), q + 1); |
129 |
p = r; |
130 |
} |
131 |
|
132 |
command_success_nodata (si, "%-20s %s", "--------------------", "-----"); |
133 |
command_success_nodata (si, _("End of \2%s\2 TEMPLATE listing."), mc->name); |
134 |
} |
135 |
else |
136 |
command_success_nodata (si, _("No templates set on channel \2%s\2."), mc->name); |
137 |
if (operoverride) |
138 |
logcommand (si, CMDLOG_ADMIN, "%s TEMPLATE (oper override)", mc->name); |
139 |
else |
140 |
logcommand (si, CMDLOG_GET, "%s TEMPLATE", mc->name); |
141 |
} |
142 |
else |
143 |
{ |
144 |
char *flagstr = parv[2]; |
145 |
|
146 |
if (!si->smu) |
147 |
{ |
148 |
command_fail (si, fault::noprivs, _("You are not logged in.")); |
149 |
return; |
150 |
} |
151 |
|
152 |
/* probably no need to special-case founder here -- jilles */ |
153 |
/* founder may always set flags -- jilles */ |
154 |
restrictflags = chanacs_source_flags (mc, si); |
155 |
if (restrictflags & CA_FOUNDER) |
156 |
restrictflags = ca_all; |
157 |
else |
158 |
{ |
159 |
if (!(restrictflags & CA_FLAGS)) |
160 |
{ |
161 |
command_fail (si, fault::noprivs, _("You are not authorized to execute this command.")); |
162 |
return; |
163 |
} |
164 |
restrictflags = allow_flags (restrictflags); |
165 |
} |
166 |
|
167 |
if (mc->find_metadata ("private:close:closer")) |
168 |
{ |
169 |
command_fail (si, fault::noprivs, _("\2%s\2 is closed."), channel); |
170 |
return; |
171 |
} |
172 |
|
173 |
if (!target || !flagstr) |
174 |
{ |
175 |
command_fail (si, fault::needmoreparams, _("Usage: TEMPLATE %s [target flags]"), channel); |
176 |
return; |
177 |
} |
178 |
|
179 |
if (*target == '+' || *target == '-' || *target == '=') |
180 |
{ |
181 |
command_fail (si, fault::badparams, _("Invalid template name \2%s\2."), target); |
182 |
return; |
183 |
} |
184 |
l = strlen (target); |
185 |
|
186 |
if (*flagstr == '!' && (flagstr[1] == '+' || flagstr[1] == '-' || flagstr[1] == '=')) |
187 |
{ |
188 |
changechanacs = 1; |
189 |
flagstr++; |
190 |
} |
191 |
|
192 |
if (*flagstr == '+' || *flagstr == '-' || *flagstr == '=') |
193 |
{ |
194 |
flags_make_bitmasks (flagstr, chanacs_flags, &addflags, &removeflags); |
195 |
if (addflags == 0 && removeflags == 0) |
196 |
{ |
197 |
command_fail (si, fault::badparams, _("No valid flags given, use /%s%s HELP FLAGS for a list"), ircd->uses_rcommand ? "" : "msg ", chansvs.disp); |
198 |
return; |
199 |
} |
200 |
} |
201 |
else |
202 |
{ |
203 |
/* allow copying templates as well */ |
204 |
addflags = get_template_flags (mc, flagstr); |
205 |
if (addflags == 0) |
206 |
{ |
207 |
command_fail (si, fault::nosuch_key, _("Invalid template name given, use /%s%s TEMPLATE %s for a list"), ircd->uses_rcommand ? "" : "msg ", chansvs.disp, mc->name); |
208 |
return; |
209 |
} |
210 |
removeflags = ca_all & ~addflags; |
211 |
} |
212 |
|
213 |
/* if adding +F, also add +f */ |
214 |
if (addflags & CA_FOUNDER) |
215 |
addflags |= CA_FLAGS, removeflags &= ~CA_FLAGS; |
216 |
/* if removing +f, also remove +F */ |
217 |
else if (removeflags & CA_FLAGS) |
218 |
removeflags |= CA_FOUNDER, addflags &= ~CA_FOUNDER; |
219 |
|
220 |
found = denied = false; |
221 |
oldflags = 0; |
222 |
|
223 |
md = mc->find_metadata ("private:templates"); |
224 |
if (md != NULL) |
225 |
{ |
226 |
p = md->value; |
227 |
strlcpy (newstr, p, sizeof newstr); |
228 |
while (p != NULL) |
229 |
{ |
230 |
while (*p == ' ') |
231 |
p++; |
232 |
q = strchr (p, '='); |
233 |
if (q == NULL) |
234 |
break; |
235 |
r = strchr (q, ' '); |
236 |
if (r != NULL && r < q) |
237 |
break; |
238 |
strlcpy (ss, q, sizeof ss); |
239 |
if (r != NULL && r - q < (int) (sizeof ss - 1)) |
240 |
{ |
241 |
ss[r - q] = '\0'; |
242 |
} |
243 |
if (q - p == l && !strncasecmp (target, p, q - p)) |
244 |
{ |
245 |
found = true; |
246 |
oldflags = flags_to_bitmask (ss, chanacs_flags, 0); |
247 |
addflags &= ~oldflags; |
248 |
removeflags &= oldflags & ~addflags; |
249 |
/* no change? */ |
250 |
if ((addflags | removeflags) == 0) |
251 |
break; |
252 |
/* attempting to add bad flag? */ |
253 |
/* attempting to remove bad flag? */ |
254 |
/* attempting to manipulate something with more privs? */ |
255 |
if (~restrictflags & addflags || ~restrictflags & removeflags || ~restrictflags & oldflags) |
256 |
{ |
257 |
denied = true; |
258 |
break; |
259 |
} |
260 |
newflags = (oldflags | addflags) & ~removeflags; |
261 |
if (newflags == 0) |
262 |
{ |
263 |
if (p == md->value) |
264 |
/* removing first entry, |
265 |
* zap the space after it */ |
266 |
strlcpy (newstr, r != NULL ? r + 1 : "", sizeof newstr); |
267 |
else |
268 |
{ |
269 |
/* otherwise, zap the space before it */ |
270 |
p--; |
271 |
strlcpy (newstr + (p - md->value), r != NULL ? r : "", sizeof newstr - (p - md->value)); |
272 |
} |
273 |
} |
274 |
else |
275 |
snprintf (newstr + (p - md->value), sizeof newstr - (p - md->value), "%s=%s%s", target, bitmask_to_flags (newflags, chanacs_flags), r != NULL ? r : ""); |
276 |
break; |
277 |
} |
278 |
p = r; |
279 |
} |
280 |
} |
281 |
if (!found) |
282 |
{ |
283 |
if (l == 3 && (!strcasecmp (target, "SOP") || !strcasecmp (target, "AOP") || !strcasecmp (target, "HOP") || !strcasecmp (target, "VOP"))) |
284 |
{ |
285 |
oldflags = get_template_flags (NULL, target); |
286 |
addflags &= ~oldflags; |
287 |
removeflags &= oldflags & ~addflags; |
288 |
newflags = (oldflags | addflags) & ~removeflags; |
289 |
if (newflags == 0) |
290 |
removeflags = 0; |
291 |
if ((addflags | removeflags) != 0) |
292 |
command_success_nodata (si, _("Redefining built-in template \2%s\2."), target); |
293 |
} |
294 |
else |
295 |
{ |
296 |
removeflags = 0; |
297 |
newflags = addflags; |
298 |
} |
299 |
if ((addflags | removeflags) == 0) |
300 |
; |
301 |
else if (~restrictflags & addflags || ~restrictflags & removeflags || ~restrictflags & oldflags) |
302 |
denied = true; |
303 |
else if (md != NULL) |
304 |
snprintf (newstr + strlen (newstr), sizeof newstr - strlen (newstr), " %s=%s", target, bitmask_to_flags (newflags, chanacs_flags)); |
305 |
else |
306 |
snprintf (newstr, sizeof newstr, "%s=%s", target, bitmask_to_flags (newflags, chanacs_flags)); |
307 |
} |
308 |
if ((addflags | removeflags) == 0) |
309 |
{ |
310 |
if (oldflags != 0) |
311 |
command_fail (si, fault::nochange, _("Template \2%s\2 on \2%s\2 unchanged."), target, channel); |
312 |
else |
313 |
command_fail (si, fault::nosuch_key, _("No such template \2%s\2 on \2%s\2."), target, channel); |
314 |
return; |
315 |
} |
316 |
if (denied) |
317 |
{ |
318 |
command_fail (si, fault::noprivs, _("You are not allowed to set \2%s\2 on template \2%s\2 in \2%s\2."), bitmask_to_flags2 (addflags, removeflags, chanacs_flags), target, mc->name); |
319 |
return; |
320 |
} |
321 |
if (strlen (newstr) >= 300) |
322 |
{ |
323 |
command_fail (si, fault::toomany, _("Sorry, too many templates on \2%s\2."), channel); |
324 |
return; |
325 |
} |
326 |
if (newstr[0] == '\0') |
327 |
mc->del_metadata ("private:templates"); |
328 |
else |
329 |
mc->add_metadata ("private:templates", newstr); |
330 |
if (oldflags == 0) |
331 |
command_success_nodata (si, _("Added template \2%s\2 with flags \2%s\2 in \2%s\2."), target, bitmask_to_flags (newflags, chanacs_flags), channel); |
332 |
else if (newflags == 0) |
333 |
command_success_nodata (si, _("Removed template \2%s\2 from \2%s\2."), target, channel); |
334 |
else |
335 |
command_success_nodata (si, _("Changed template \2%s\2 to \2%s\2 in \2%s\2."), target, bitmask_to_flags (newflags, chanacs_flags), channel); |
336 |
|
337 |
flagstr = bitmask_to_flags2 (addflags, removeflags, chanacs_flags); |
338 |
if (changechanacs) |
339 |
{ |
340 |
node_t *n, *tn; |
341 |
chanacs_t *ca; |
342 |
int changes = 0, founderskipped = 0; |
343 |
char flagstr2[128]; |
344 |
|
345 |
LIST_FOREACH_SAFE (n, tn, mc->chanacs.head) |
346 |
{ |
347 |
ca = static_cast<chanacs_t *> (n->data); |
348 |
if (ca->level != oldflags) |
349 |
continue; |
350 |
if ((addflags | removeflags) & CA_FOUNDER) |
351 |
{ |
352 |
founderskipped++; |
353 |
continue; |
354 |
} |
355 |
changes++; |
356 |
chanacs_modify_simple (ca, newflags, ~newflags); |
357 |
chanacs_close (ca); |
358 |
} |
359 |
logcommand (si, CMDLOG_SET, "%s TEMPLATE %s !%s (%d changes)", mc->name, target, flagstr, changes); |
360 |
strlcpy (flagstr2, flagstr, sizeof flagstr2); |
361 |
if (changes > 0) |
362 |
verbose (mc, "\2%s\2 set \2%s\2 on %d access entries with flags \2%s\2.", get_source_name (si), flagstr2, changes, bitmask_to_flags (oldflags, chanacs_flags)); |
363 |
command_success_nodata (si, _("%d access entries updated accordingly."), changes); |
364 |
if (founderskipped) |
365 |
command_success_nodata (si, _("Not updating %d access entries involving founder status. Please do it manually."), founderskipped); |
366 |
} |
367 |
else |
368 |
logcommand (si, CMDLOG_SET, "%s TEMPLATE %s %s", mc->name, target, flagstr); |
369 |
/*verbose(mc, "Flags \2%s\2 were set on template \2%s\2 in \2%s\2.", flagstr, target, channel); */ |
370 |
} |
371 |
} |