ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/chanserv/xop.C
Revision: 1.10
Committed: Sat Sep 22 14:27:27 2007 UTC (16 years, 8 months ago) by pippijn
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.9: +3 -3 lines
Log Message:
split up ermyth into ermyth-modules, libermyth (currently just ermyth-util) and ermyth-core

File Contents

# Content
1 /**
2 * xop.C: This file contains code for the ChanServ XOP 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-2007 Atheme Development Group
10 * Rights to this code are as documented in doc/pod/license.pod.
11 *
12 * $Id: xop.C,v 1.9 2007-09-16 18:54:43 pippijn Exp $
13 */
14
15 #include "atheme.h"
16 #include <ermyth/module.h>
17 #include <account/mychan.h>
18 #include <account/myuser.h>
19 #include <account/chanacs.h>
20 #include "template.h"
21
22 static char const rcsid[] = "$Id: xop.C,v 1.9 2007-09-16 18:54:43 pippijn Exp $";
23
24 REGISTER_MODULE ("chanserv/xop", false, "The Ermyth Team <http://ermyth.xinutec.org>");
25
26 /* the individual command stuff, now that we've reworked, hardcode ;) --w00t */
27 static void cs_xop_do_add (sourceinfo_t *si, mychan_t *mc, myuser_t *mu, char *target, unsigned int level, char const * const leveldesc, unsigned int restrictflags);
28 static void cs_xop_do_del (sourceinfo_t *si, mychan_t *mc, myuser_t *mu, char *target, unsigned int level, char const * const leveldesc);
29 static void cs_xop_do_list (sourceinfo_t *si, mychan_t *mc, unsigned int level, char const * const leveldesc, int operoverride);
30
31 static void cs_cmd_sop (sourceinfo_t *si, int parc, char *parv[]);
32 static void cs_cmd_aop (sourceinfo_t *si, int parc, char *parv[]);
33 static void cs_cmd_hop (sourceinfo_t *si, int parc, char *parv[]);
34 static void cs_cmd_vop (sourceinfo_t *si, int parc, char *parv[]);
35 static void cs_cmd_forcexop (sourceinfo_t *si, int parc, char *parv[]);
36
37 command_t const cs_sop = { "SOP", N_("Manipulates a channel SOP list."), AC_NONE, 3, cs_cmd_sop };
38 command_t const cs_aop = { "AOP", N_("Manipulates a channel AOP list."), AC_NONE, 3, cs_cmd_aop };
39 command_t const cs_hop = { "HOP", N_("Manipulates a channel HOP list."), AC_NONE, 3, cs_cmd_hop };
40 command_t const cs_vop = { "VOP", N_("Manipulates a channel VOP list."), AC_NONE, 3, cs_cmd_vop };
41 command_t const cs_forcexop = { "FORCEXOP", N_("Forces access levels to xOP levels."), AC_NONE, 1, cs_cmd_forcexop };
42
43 E cmdvec cs_cmdtree;
44 E helpvec cs_helptree;
45
46 bool
47 _modinit (module *m)
48 {
49 cs_cmdtree << cs_aop;
50 cs_cmdtree << cs_sop;
51 if (ircd && ircd->uses_halfops)
52 cs_cmdtree << cs_hop;
53 cs_cmdtree << cs_vop;
54 cs_cmdtree << cs_forcexop;
55
56 help_addentry (cs_helptree, "SOP", "help/chanserv/sop", NULL);
57 help_addentry (cs_helptree, "AOP", "help/chanserv/aop", NULL);
58 help_addentry (cs_helptree, "VOP", "help/chanserv/vop", NULL);
59 if (ircd && ircd->uses_halfops)
60 help_addentry (cs_helptree, "HOP", "help/chanserv/hop", NULL);
61 help_addentry (cs_helptree, "FORCEXOP", "help/chanserv/forcexop", NULL);
62
63 return true;
64 }
65
66 void
67 _moddeinit ()
68 {
69 cs_cmdtree >> cs_aop;
70 cs_cmdtree >> cs_sop;
71 if (ircd && ircd->uses_halfops)
72 cs_cmdtree >> cs_hop;
73 cs_cmdtree >> cs_vop;
74 cs_cmdtree >> cs_forcexop;
75
76 help_delentry (cs_helptree, "SOP");
77 help_delentry (cs_helptree, "AOP");
78 help_delentry (cs_helptree, "VOP");
79 if (ircd && ircd->uses_halfops)
80 help_delentry (cs_helptree, "HOP");
81 help_delentry (cs_helptree, "FORCEXOP");
82 }
83
84 static void
85 cs_xop (sourceinfo_t *si, int parc, char *parv[], char const * const leveldesc)
86 {
87 myuser_t *mu;
88 mychan_t *mc;
89 int operoverride = 0;
90 unsigned int restrictflags;
91 char *chan = parv[0];
92 char *cmd = parv[1];
93 char *uname = parv[2];
94 unsigned int level;
95
96 if (!cmd || !chan)
97 {
98 command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "xOP");
99 command_fail (si, fault::needmoreparams, _("Syntax: SOP|AOP|HOP|VOP <#channel> ADD|DEL|LIST <nickname>"));
100 return;
101 }
102
103 if ((strcasecmp ("LIST", cmd)) && (!uname))
104 {
105 command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "xOP");
106 command_fail (si, fault::needmoreparams, _("Syntax: SOP|AOP|HOP|VOP <#channel> ADD|DEL|LIST <nickname>"));
107 return;
108 }
109
110 /* make sure they're registered, logged in
111 * and the founder of the channel before
112 * we go any further.
113 */
114 if (!si->smu)
115 {
116 /* if they're opers and just want to LIST, they don't have to log in */
117 if (!(has_priv (si, PRIV_CHAN_AUSPEX) && !strcasecmp ("LIST", cmd)))
118 {
119 command_fail (si, fault::noprivs, _("You are not logged in."));
120 return;
121 }
122 }
123
124 mc = mychan_t::find (chan);
125 if (!mc)
126 {
127 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), chan);
128 return;
129 }
130
131 if (mc->find_metadata ("private:close:closer") && (!has_priv (si, PRIV_CHAN_AUSPEX) || strcasecmp ("LIST", cmd)))
132 {
133 command_fail (si, fault::noprivs, _("\2%s\2 is closed."), chan);
134 return;
135 }
136
137 level = get_template_flags (mc, leveldesc);
138 if (level & CA_FOUNDER)
139 {
140 command_fail (si, fault::noprivs, _("\2%s\2 %s template has founder flag, not allowing xOP command."), chan, leveldesc);
141 return;
142 }
143
144 /* ADD */
145 if (!strcasecmp ("ADD", cmd))
146 {
147 mu = myuser_t::find_ext (uname);
148
149 /* As in /cs flags, allow founder to do anything */
150 restrictflags = chanacs_source_flags (mc, si);
151 if (restrictflags & CA_FOUNDER)
152 restrictflags = ca_all;
153 /* The following is a bit complicated, to allow for
154 * possible future denial of granting +f */
155 if (!(restrictflags & CA_FLAGS))
156 {
157 command_fail (si, fault::noprivs, _("You are not authorized to perform this operation."));
158 return;
159 }
160 restrictflags = allow_flags (restrictflags);
161 if ((restrictflags & level) != level)
162 {
163 command_fail (si, fault::noprivs, _("You are not authorized to perform this operation."));
164 return;
165 }
166 cs_xop_do_add (si, mc, mu, uname, level, leveldesc, restrictflags);
167 }
168
169 else if (!strcasecmp ("DEL", cmd))
170 {
171 mu = myuser_t::find_ext (uname);
172
173 /* As in /cs flags, allow founder to do anything -- fix for #64: allow self removal. */
174 restrictflags = chanacs_source_flags (mc, si);
175 if (restrictflags & CA_FOUNDER || mu == si->smu)
176 restrictflags = ca_all;
177 /* The following is a bit complicated, to allow for
178 * possible future denial of granting +f */
179 if (!(restrictflags & CA_FLAGS))
180 {
181 command_fail (si, fault::noprivs, _("You are not authorized to perform this operation."));
182 return;
183 }
184 restrictflags = allow_flags (restrictflags);
185 if ((restrictflags & level) != level)
186 {
187 command_fail (si, fault::noprivs, _("You are not authorized to perform this operation."));
188 return;
189 }
190 cs_xop_do_del (si, mc, mu, uname, level, leveldesc);
191 }
192
193 else if (!strcasecmp ("LIST", cmd))
194 {
195 if (!chanacs_source_has_flag (mc, si, CA_ACLVIEW))
196 {
197 if (has_priv (si, PRIV_CHAN_AUSPEX))
198 operoverride = 1;
199 else
200 {
201 command_fail (si, fault::noprivs, _("You are not authorized to perform this operation."));
202 return;
203 }
204 }
205 cs_xop_do_list (si, mc, level, leveldesc, operoverride);
206 }
207 }
208
209 static void
210 cs_cmd_sop (sourceinfo_t *si, int parc, char *parv[])
211 {
212 cs_xop (si, parc, parv, "SOP");
213 }
214
215 static void
216 cs_cmd_aop (sourceinfo_t *si, int parc, char *parv[])
217 {
218 cs_xop (si, parc, parv, "AOP");
219 }
220
221 static void
222 cs_cmd_vop (sourceinfo_t *si, int parc, char *parv[])
223 {
224 cs_xop (si, parc, parv, "VOP");
225 }
226
227 static void
228 cs_cmd_hop (sourceinfo_t *si, int parc, char *parv[])
229 {
230 /* Don't reject the command. This helps the rare case where
231 * a network switches to a non-halfop ircd: users can still
232 * remove pre-transition HOP entries.
233 */
234 if (!ircd->uses_halfops && si->su != NULL)
235 notice (chansvs.nick, si->su->nick, "Warning: Your IRC server does not support halfops.");
236
237 cs_xop (si, parc, parv, "HOP");
238 }
239
240
241 static void
242 cs_xop_do_add (sourceinfo_t *si, mychan_t *mc, myuser_t *mu, char *target, unsigned int level, char const * const leveldesc, unsigned int restrictflags)
243 {
244 char hostbuf[BUFSIZE];
245 chanuser_t *cu;
246 chanacs_t *ca;
247 node_t *n;
248 unsigned int addflags = level, removeflags = ~level;
249 bool isnew;
250
251 if (!mu)
252 {
253 /* we might be adding a hostmask */
254 if (!validhostmask (target))
255 {
256 command_fail (si, fault::badparams, _("\2%s\2 is neither a registered account nor a hostmask."), target);
257 return;
258 }
259
260 target = collapse (target);
261 ca = chanacs_open (mc, NULL, target, true);
262 if (ca->level == level)
263 {
264 command_fail (si, fault::nochange, _("\2%s\2 is already on the %s list for \2%s\2"), target, leveldesc, mc->name);
265 return;
266 }
267 isnew = ca->level == 0;
268
269 if (isnew && chanacs_is_table_full (ca))
270 {
271 command_fail (si, fault::toomany, _("Channel %s access list is full."), mc->name);
272 chanacs_close (ca);
273 return;
274 }
275
276 if (!chanacs_modify (ca, &addflags, &removeflags, restrictflags))
277 {
278 command_fail (si, fault::noprivs, _("You are not authorized to modify the access entry for \2%s\2 on \2%s\2."), target, mc->name);
279 chanacs_close (ca);
280 return;
281 }
282 chanacs_close (ca);
283 if (!isnew)
284 {
285 /* they have access? change it! */
286 logcommand (si, CMDLOG_SET, "%s %s ADD %s (changed access)", mc->name, leveldesc, target);
287 command_success_nodata (si, _("\2%s\2's access on \2%s\2 has been changed to \2%s\2."), target, mc->name, leveldesc);
288 verbose (mc, "\2%s\2 changed \2%s\2's access to \2%s\2.", get_source_name (si), target, leveldesc);
289 }
290 else
291 {
292 logcommand (si, CMDLOG_SET, "%s %s ADD %s", mc->name, leveldesc, target);
293 command_success_nodata (si, _("\2%s\2 has been added to the %s list for \2%s\2."), target, leveldesc, mc->name);
294 verbose (mc, "\2%s\2 added \2%s\2 to the %s list.", get_source_name (si), target, leveldesc);
295 }
296
297 /* run through the channel's user list and do it */
298 /* make sure the channel exists */
299 if (mc->chan == NULL)
300 return;
301 LIST_FOREACH (n, mc->chan->members.head)
302 {
303 cu = (chanuser_t *) n->data;
304
305 strlcpy (hostbuf, cu->user->nick, BUFSIZE);
306 strlcat (hostbuf, "!", BUFSIZE);
307 strlcat (hostbuf, cu->user->user, BUFSIZE);
308 strlcat (hostbuf, "@", BUFSIZE);
309 strlcat (hostbuf, cu->user->vhost, BUFSIZE);
310
311 if (match (target, hostbuf))
312 continue;
313
314 if (level & CA_AUTOOP)
315 {
316 if (!(cu->modes & CMODE_OP))
317 {
318 modestack_mode_param (chansvs.nick, mc->chan, MTYPE_ADD, 'o', CLIENT_NAME (cu->user));
319 cu->modes |= CMODE_OP;
320 }
321 }
322 else if (ircd->uses_halfops && level & CA_AUTOHALFOP)
323 {
324 if (!(cu->modes & (CMODE_OP | ircd->halfops_mode)))
325 {
326 modestack_mode_param (chansvs.nick, mc->chan, MTYPE_ADD, ircd->halfops_mchar[1], CLIENT_NAME (cu->user));
327 cu->modes |= ircd->halfops_mode;
328 }
329 }
330 else if (level & (CA_AUTOVOICE | CA_AUTOHALFOP))
331 {
332 /* XXX HOP should have +V */
333 if (!(cu->modes & (CMODE_OP | ircd->halfops_mode | CMODE_VOICE)))
334 {
335 modestack_mode_param (chansvs.nick, mc->chan, MTYPE_ADD, 'v', CLIENT_NAME (cu->user));
336 cu->modes |= CMODE_VOICE;
337 }
338 }
339 }
340 return;
341 }
342
343 ca = chanacs_open (mc, mu, NULL, true);
344
345 if (ca->level & CA_FOUNDER)
346 {
347 command_fail (si, fault::noprivs, _("\2%s\2 is the founder for \2%s\2 and may not be added to the %s list."), mu->name, mc->name, leveldesc);
348 return;
349 }
350
351 ca = chanacs_open (mc, mu, NULL, true);
352 if (ca->level == level)
353 {
354 command_fail (si, fault::nochange, _("\2%s\2 is already on the %s list for \2%s\2."), mu->name, leveldesc, mc->name);
355 return;
356 }
357
358 /* NEVEROP logic moved here
359 * Allow changing access level, but not adding
360 * -- jilles */
361 if (mu->flags & MU_NEVEROP && (ca->level == 0 || ca->level == CA_AKICK))
362 {
363 command_fail (si, fault::noprivs, _("\2%s\2 does not wish to be added to access lists (NEVEROP set)."), mu->name);
364 chanacs_close (ca);
365 return;
366 }
367
368 /*
369 * this is a little more cryptic than it used to be, but much cleaner. Functionally should be
370 * the same, with the exception that if they had access before, now it doesn't tell what it got
371 * changed from (I considered the effort to put an extra lookup in not worth it. --w00t
372 */
373 /* just assume there's just one entry for that user -- jilles */
374
375 isnew = ca->level == 0;
376 if (isnew && chanacs_is_table_full (ca))
377 {
378 command_fail (si, fault::toomany, _("Channel %s access list is full."), mc->name);
379 chanacs_close (ca);
380 return;
381 }
382
383 if (!chanacs_modify (ca, &addflags, &removeflags, restrictflags))
384 {
385 command_fail (si, fault::noprivs, _("You are not authorized to modify the access entry for \2%s\2 on \2%s\2."), mu->name, mc->name);
386 chanacs_close (ca);
387 return;
388 }
389 chanacs_close (ca);
390
391 if (!isnew)
392 {
393 /* they have access? change it! */
394 logcommand (si, CMDLOG_SET, "%s %s ADD %s (changed access)", mc->name, leveldesc, mu->name);
395 command_success_nodata (si, _("\2%s\2's access on \2%s\2 has been changed to \2%s\2."), mu->name, mc->name, leveldesc);
396 verbose (mc, "\2%s\2 changed \2%s\2's access to \2%s\2.", get_source_name (si), mu->name, leveldesc);
397 }
398 else
399 {
400 /* they have no access, add */
401 logcommand (si, CMDLOG_SET, "%s %s ADD %s", mc->name, leveldesc, mu->name);
402 command_success_nodata (si, _("\2%s\2 has been added to the %s list for \2%s\2."), mu->name, leveldesc, mc->name);
403 verbose (mc, "\2%s\2 added \2%s\2 to the %s list.", get_source_name (si), mu->name, leveldesc);
404 }
405
406 /* run through the channel's user list and do it */
407 /* make sure the channel exists */
408 if (mc->chan == NULL)
409 return;
410 LIST_FOREACH (n, mc->chan->members.head)
411 {
412 cu = (chanuser_t *) n->data;
413
414 if (cu->user->myuser != mu)
415 continue;
416
417 if (level & CA_AUTOOP)
418 {
419 if (!(cu->modes & CMODE_OP))
420 {
421 modestack_mode_param (chansvs.nick, mc->chan, MTYPE_ADD, 'o', CLIENT_NAME (cu->user));
422 cu->modes |= CMODE_OP;
423 }
424 }
425 else if (ircd->uses_halfops && level & CA_AUTOHALFOP)
426 {
427 if (!(cu->modes & (CMODE_OP | ircd->halfops_mode)))
428 {
429 modestack_mode_param (chansvs.nick, mc->chan, MTYPE_ADD, ircd->halfops_mchar[1], CLIENT_NAME (cu->user));
430 cu->modes |= ircd->halfops_mode;
431 }
432 }
433 else if (level & (CA_AUTOVOICE | CA_AUTOHALFOP))
434 {
435 /* XXX HOP should have +V */
436 if (!(cu->modes & (CMODE_OP | ircd->halfops_mode | CMODE_VOICE)))
437 {
438 modestack_mode_param (chansvs.nick, mc->chan, MTYPE_ADD, 'v', CLIENT_NAME (cu->user));
439 cu->modes |= CMODE_VOICE;
440 }
441 }
442 }
443 }
444
445 static void
446 cs_xop_do_del (sourceinfo_t *si, mychan_t *mc, myuser_t *mu, char *target, unsigned int level, char const * const leveldesc)
447 {
448 chanacs_t *ca;
449
450 /* let's finally make this sane.. --w00t */
451 if (!mu)
452 {
453 /* we might be deleting a hostmask */
454 if (!validhostmask (target))
455 {
456 command_fail (si, fault::badparams, _("\2%s\2 is neither a nickname nor a hostmask."), target);
457 return;
458 }
459
460 ca = chanacs_find_host_literal (mc, target, level);
461 if (ca == NULL || ca->level != level)
462 {
463 command_fail (si, fault::nochange, _("\2%s\2 is not on the %s list for \2%s\2."), target, leveldesc, mc->name);
464 return;
465 }
466
467 object_unref (ca);
468 verbose (mc, "\2%s\2 removed \2%s\2 from the %s list.", get_source_name (si), target, leveldesc);
469 logcommand (si, CMDLOG_SET, "%s %s DEL %s", mc->name, leveldesc, target);
470 command_success_nodata (si, _("\2%s\2 has been removed from the %s list for \2%s\2."), target, leveldesc, mc->name);
471 return;
472 }
473
474 if (!(ca = chanacs_find (mc, mu, level)) || ca->level != level)
475 {
476 command_fail (si, fault::nochange, _("\2%s\2 is not on the %s list for \2%s\2."), mu->name, leveldesc, mc->name);
477 return;
478 }
479
480 object_unref (ca);
481 command_success_nodata (si, _("\2%s\2 has been removed from the %s list for \2%s\2."), mu->name, leveldesc, mc->name);
482 logcommand (si, CMDLOG_SET, "%s %s DEL %s", mc->name, leveldesc, mu->name);
483 verbose (mc, "\2%s\2 removed \2%s\2 from the %s list.", get_source_name (si), mu->name, leveldesc);
484 }
485
486
487 static void
488 cs_xop_do_list (sourceinfo_t *si, mychan_t *mc, unsigned int level, char const * const leveldesc, int operoverride)
489 {
490 chanacs_t *ca;
491 int i = 0;
492 node_t *n;
493
494 command_success_nodata (si, _("%s list for \2%s\2:"), leveldesc, mc->name);
495 LIST_FOREACH (n, mc->chanacs.head)
496 {
497 ca = (chanacs_t *) n->data;
498 if (ca->level == level)
499 {
500 if (!ca->myuser)
501 command_success_nodata (si, "%d: \2%s\2", ++i, ca->host);
502 else if (!ca->myuser->logins.empty ())
503 command_success_nodata (si, _("%d: \2%s\2 (logged in)"), ++i, ca->myuser->name);
504 else
505 command_success_nodata (si, _("%d: \2%s\2 (not logged in)"), ++i, ca->myuser->name);
506 }
507 }
508 /* XXX */
509 command_success_nodata (si, _("Total of \2%d\2 %s in %s list of \2%s\2."), i, (i == 1) ? "entry" : "entries", leveldesc, mc->name);
510 if (operoverride)
511 logcommand (si, CMDLOG_ADMIN, "%s %s LIST (oper override)", mc->name, leveldesc);
512 else
513 logcommand (si, CMDLOG_GET, "%s %s LIST", mc->name, leveldesc);
514 }
515
516 static void
517 cs_cmd_forcexop (sourceinfo_t *si, int parc, char *parv[])
518 {
519 char *chan = parv[0];
520 chanacs_t *ca;
521 mychan_t *mc = mychan_t::find (chan);
522 node_t *n;
523 int changes;
524 unsigned int newlevel;
525 char const *desc;
526 unsigned int ca_sop, ca_aop, ca_hop, ca_vop;
527
528 if (!chan)
529 {
530 command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "FORCEXOP");
531 command_fail (si, fault::needmoreparams, _("Syntax: FORCEXOP <#channel>"));
532 return;
533 }
534
535 if (!mc)
536 {
537 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), chan);
538 return;
539 }
540
541 if (mc->find_metadata ("private:close:closer"))
542 {
543 command_fail (si, fault::noprivs, _("\2%s\2 is closed."), chan);
544 return;
545 }
546
547 if (!si->smu->is_founder (mc))
548 {
549 command_fail (si, fault::noprivs, _("You are not authorized to perform this operation."));
550 return;
551 }
552
553 ca_sop = get_template_flags (mc, "SOP");
554 ca_aop = get_template_flags (mc, "AOP");
555 ca_hop = get_template_flags (mc, "HOP");
556 ca_vop = get_template_flags (mc, "VOP");
557
558 changes = 0;
559 LIST_FOREACH (n, mc->chanacs.head)
560 {
561 ca = (chanacs_t *) n->data;
562
563 if (ca->level & CA_AKICK)
564 continue;
565 if (ca->level & CA_FOUNDER)
566 newlevel = CA_INITIAL & ca_all, desc = "Founder";
567 else if (!(~ca->level & ca_sop))
568 newlevel = ca_sop, desc = "SOP";
569 else if (ca->level == ca_aop)
570 newlevel = ca_aop, desc = "AOP";
571 else if (ca->level == ca_hop)
572 newlevel = ca_hop, desc = "HOP";
573 else if (ca->level == ca_vop)
574 newlevel = ca_vop, desc = "VOP";
575 else if (ca->level & (CA_SET | CA_RECOVER | CA_FLAGS))
576 newlevel = ca_sop, desc = "SOP";
577 else if (ca->level & (CA_OP | CA_AUTOOP | CA_REMOVE))
578 newlevel = ca_aop, desc = "AOP";
579 else if (ca->level & (CA_HALFOP | CA_AUTOHALFOP | CA_TOPIC))
580 {
581 if (ca_hop == ca_vop)
582 newlevel = ca_aop, desc = "AOP";
583 else
584 newlevel = ca_hop, desc = "HOP";
585 }
586 else /*if (ca->level & CA_AUTOVOICE) */
587 newlevel = ca_vop, desc = "VOP";
588 #if 0
589 else
590 newlevel = 0;
591 #endif
592 if (newlevel == ca->level)
593 continue;
594 changes++;
595 command_success_nodata (si, "%s: %s -> %s", ca->myuser ? ca->myuser->name : ca->host, bitmask_to_flags (ca->level, chanacs_flags), desc);
596 chanacs_modify_simple (ca, newlevel, ~newlevel);
597 }
598 command_success_nodata (si, _("FORCEXOP \2%s\2 done (\2%d\2 changes)"), mc->name, changes);
599 if (changes > 0)
600 verbose (mc, "\2%s\2 reset access levels to xOP (\2%d\2 changes)", get_source_name (si), changes);
601 logcommand (si, CMDLOG_SET, "%s FORCEXOP (%d changes)", mc->name, changes);
602 }