ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/ermyth/modules/chanserv/set.C
Revision: 1.11
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.10: +5 -3 lines
Log Message:
split up ermyth into ermyth-modules, libermyth (currently just ermyth-util) and ermyth-core

File Contents

# Content
1 /**
2 * set.C: This file contains routines to handle the ChanServ SET command.
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 © 2003-2004 E. Will et al.
10 * Rights to this code are documented in doc/pod/license.pod.
11 *
12 * $Id: set.C,v 1.10 2007-09-16 18:54:43 pippijn Exp $
13 */
14
15 #include "atheme.h"
16 #include <util/numeric.h>
17 #include <libermyth.h>
18 #include "confparse.h"
19 #include <ermyth/module.h>
20 #include <account/myuser.h>
21 #include <account/chanacs.h>
22 #include <account/mychan.h>
23
24 static char const rcsid[] = "$Id: set.C,v 1.10 2007-09-16 18:54:43 pippijn Exp $";
25
26 static void on_config_ready (void);
27
28 REGISTER_MODULE ("chanserv/set", false, "The Ermyth Team <http://ermyth.xinutec.org>");
29
30 static void cs_help_set (sourceinfo_t *si);
31 static void cs_cmd_set (sourceinfo_t *si, int parc, char *parv[]);
32
33 static void cs_cmd_set_email (sourceinfo_t *si, int parc, char *parv[]);
34 static void cs_cmd_set_url (sourceinfo_t *si, int parc, char *parv[]);
35 static void cs_cmd_set_entrymsg (sourceinfo_t *si, int parc, char *parv[]);
36 static void cs_cmd_set_founder (sourceinfo_t *si, int parc, char *parv[]);
37 static void cs_cmd_set_mlock (sourceinfo_t *si, int parc, char *parv[]);
38 static void cs_cmd_set_keeptopic (sourceinfo_t *si, int parc, char *parv[]);
39 static void cs_cmd_set_topiclock (sourceinfo_t *si, int parc, char *parv[]);
40 static void cs_cmd_set_secure (sourceinfo_t *si, int parc, char *parv[]);
41 static void cs_cmd_set_verbose (sourceinfo_t *si, int parc, char *parv[]);
42 static void cs_cmd_set_fantasy (sourceinfo_t *si, int parc, char *parv[]);
43 static void cs_cmd_set_staffonly (sourceinfo_t *si, int parc, char *parv[]);
44 static void cs_cmd_set_property (sourceinfo_t *si, int parc, char *parv[]);
45 static void cs_cmd_set_guard (sourceinfo_t *si, int parc, char *parv[]);
46
47 command_t const cs_set = { "SET", N_("Sets various control flags."), AC_NONE, 3, cs_cmd_set };
48
49 command_t const cs_set_founder = { "FOUNDER", N_("Transfers foundership of a channel."), AC_NONE, 2, cs_cmd_set_founder };
50 command_t const cs_set_mlock = { "MLOCK", N_("Sets channel mode lock."), AC_NONE, 2, cs_cmd_set_mlock };
51 command_t const cs_set_secure = { "SECURE", N_("Prevents unauthorized users from gaining operator status."), AC_NONE, 2, cs_cmd_set_secure };
52 command_t const cs_set_verbose = { "VERBOSE", N_("Notifies channel about access list modifications."), AC_NONE, 2, cs_cmd_set_verbose };
53 command_t const cs_set_url = { "URL", N_("Sets the channel URL."), AC_NONE, 2, cs_cmd_set_url };
54 command_t const cs_set_entrymsg = { "ENTRYMSG", N_("Sets the channel's entry message."), AC_NONE, 2, cs_cmd_set_entrymsg };
55 command_t const cs_set_property = { "PROPERTY", N_("Manipulates channel metadata."), AC_NONE, 2, cs_cmd_set_property };
56 command_t const cs_set_email = { "EMAIL", N_("Sets the channel e-mail address."), AC_NONE, 2, cs_cmd_set_email };
57 command_t const cs_set_keeptopic = { "KEEPTOPIC", N_("Enables topic retention."), AC_NONE, 2, cs_cmd_set_keeptopic };
58 command_t const cs_set_topiclock = { "TOPICLOCK", N_("Restricts who can change the topic."), AC_NONE, 2, cs_cmd_set_topiclock };
59 command_t cs_set_guard = { "GUARD", N_("Sets whether or not services will inhabit the channel."), AC_NONE, 2, cs_cmd_set_guard };
60 command_t const cs_set_fantasy = { "FANTASY", N_("Allows or disallows in-channel commands."), AC_NONE, 2, cs_cmd_set_fantasy };
61 command_t const cs_set_staffonly = { "STAFFONLY", N_("Sets the channel as staff-only. (Non staff is kickbanned.)"), PRIV_CHAN_ADMIN, 2, cs_cmd_set_staffonly };
62
63 command_t const *cs_set_commands[] = {
64 &cs_set_founder,
65 &cs_set_mlock,
66 &cs_set_secure,
67 &cs_set_verbose,
68 &cs_set_url,
69 &cs_set_entrymsg,
70 &cs_set_property,
71 &cs_set_email,
72 &cs_set_keeptopic,
73 &cs_set_topiclock,
74 &cs_set_guard,
75 &cs_set_fantasy,
76 &cs_set_staffonly,
77 NULL
78 };
79
80 E cmdvec cs_cmdtree;
81 E helpvec cs_helptree;
82 cmdvec cs_set_cmdtree;
83
84 static void
85 on_config_ready (void)
86 {
87 if (config_options.join_chans)
88 cs_set_guard.access = NULL;
89 else
90 cs_set_guard.access = PRIV_ADMIN;
91 }
92
93 static void
94 cs_help_set (sourceinfo_t *si)
95 {
96 command_success_nodata (si, _("Help for \2SET\2:"));
97 command_success_nodata (si, " ");
98 command_success_nodata (si, _("SET allows you to set various control flags"));
99 command_success_nodata (si, _("for channels that change the way certain"));
100 command_success_nodata (si, _("operations are performed on them."));
101 command_success_nodata (si, " ");
102 command_help (si, cs_set_cmdtree);
103 command_success_nodata (si, " ");
104 command_success_nodata (si, _("For more specific help use \2/msg %s HELP SET \37command\37\2."), si->service->disp);
105 }
106
107 /* SET <#channel> <setting> <parameters> */
108 static void
109 cs_cmd_set (sourceinfo_t *si, int parc, char *parv[])
110 {
111 char *chan;
112 char *cmd;
113 command_t const *c;
114
115 if (parc < 3)
116 {
117 command_fail (si, fault::needmoreparams, STR_INSUFFICIENT_PARAMS, "SET");
118 command_fail (si, fault::needmoreparams, _("Syntax: SET <#channel> <setting> <parameters>"));
119 return;
120 }
121
122 if (parv[0][0] == '#')
123 chan = parv[0], cmd = parv[1];
124 else if (parv[1][0] == '#')
125 cmd = parv[0], chan = parv[1];
126 else
127 {
128 command_fail (si, fault::badparams, STR_INVALID_PARAMS, "SET");
129 command_fail (si, fault::badparams, _("Syntax: SET <#channel> <setting> <parameters>"));
130 return;
131 }
132
133 c = cs_set_cmdtree.find (cmd);
134 if (c == NULL)
135 {
136 command_fail (si, fault::badparams, _("Invalid command. Use \2/%s%s help\2 for a command listing."), (ircd->uses_rcommand == false) ? "msg " : "", si->service->disp);
137 return;
138 }
139
140 parv[1] = chan;
141 c->exec (si->service, si, parc - 1, parv + 1);
142 }
143
144 static void
145 cs_cmd_set_email (sourceinfo_t *si, int parc, char *parv[])
146 {
147 mychan_t *mc;
148 char *mail = parv[1];
149
150 if (!(mc = mychan_t::find (parv[0])))
151 {
152 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), parv[0]);
153 return;
154 }
155
156 if (!chanacs_source_has_flag (mc, si, CA_SET))
157 {
158 command_fail (si, fault::noprivs, _("You are not authorized to execute this command."));
159 return;
160 }
161
162 if (!mail || !strcasecmp (mail, "NONE") || !strcasecmp (mail, "OFF"))
163 {
164 if (mc->find_metadata ("email"))
165 {
166 mc->del_metadata ("email");
167 command_success_nodata (si, _("The e-mail address for \2%s\2 was deleted."), mc->name);
168 logcommand (si, CMDLOG_SET, "%s SET EMAIL NONE", mc->name);
169 return;
170 }
171
172 command_fail (si, fault::nochange, _("The e-mail address for \2%s\2 was not set."), mc->name);
173 return;
174 }
175
176 if (strlen (mail) >= EMAILLEN)
177 {
178 command_fail (si, fault::badparams, STR_INVALID_PARAMS, "EMAIL");
179 return;
180 }
181
182 if (!validemail (mail))
183 {
184 command_fail (si, fault::badparams, _("\2%s\2 is not a valid e-mail address."), mail);
185 return;
186 }
187
188 /* we'll overwrite any existing metadata */
189 mc->add_metadata ("email", mail);
190
191 logcommand (si, CMDLOG_SET, "%s SET EMAIL %s", mc->name, mail);
192 command_success_nodata (si, _("The e-mail address for \2%s\2 has been set to \2%s\2."), parv[0], mail);
193 }
194
195 static void
196 cs_cmd_set_url (sourceinfo_t *si, int parc, char *parv[])
197 {
198 mychan_t *mc;
199 char *url = parv[1];
200
201 if (!(mc = mychan_t::find (parv[0])))
202 {
203 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), parv[0]);
204 return;
205 }
206
207 if (!chanacs_source_has_flag (mc, si, CA_SET))
208 {
209 command_fail (si, fault::noprivs, _("You are not authorized to execute this command."));
210 return;
211 }
212
213 /* XXX: I'd like to be able to use /CS SET #channel URL to clear but CS SET won't let me... */
214 if (!url || !strcasecmp ("OFF", url) || !strcasecmp ("NONE", url))
215 {
216 /* not in a namespace to allow more natural use of SET PROPERTY.
217 * they may be able to introduce spaces, though. c'est la vie.
218 */
219 if (mc->find_metadata ("url"))
220 {
221 mc->del_metadata ("url");
222 logcommand (si, CMDLOG_SET, "%s SET URL NONE", mc->name);
223 command_success_nodata (si, _("The URL for \2%s\2 has been cleared."), parv[0]);
224 return;
225 }
226
227 command_fail (si, fault::nochange, _("The URL for \2%s\2 was not set."), parv[0]);
228 return;
229 }
230
231 /* we'll overwrite any existing metadata */
232 mc->add_metadata ("url", url);
233
234 logcommand (si, CMDLOG_SET, "%s SET URL %s", mc->name, url);
235 command_success_nodata (si, _("The URL of \2%s\2 has been set to \2%s\2."), parv[0], url);
236 }
237
238 static void
239 cs_cmd_set_entrymsg (sourceinfo_t *si, int parc, char *parv[])
240 {
241 mychan_t *mc;
242
243 if (!(mc = mychan_t::find (parv[0])))
244 {
245 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), parv[0]);
246 return;
247 }
248
249 if (!chanacs_source_has_flag (mc, si, CA_SET))
250 {
251 command_fail (si, fault::noprivs, _("You are not authorized to execute this command."));
252 return;
253 }
254
255 /* XXX: I'd like to be able to use /CS SET #channel ENTRYMSG to clear but CS SET won't let me... */
256 if (!parv[1] || !strcasecmp ("OFF", parv[1]) || !strcasecmp ("NONE", parv[1]))
257 {
258 /* entrymsg is private because users won't see it if they're AKICKED,
259 * if the channel is +i, or if the channel is STAFFONLY
260 */
261 if (mc->find_metadata ("private:entrymsg"))
262 {
263 mc->del_metadata ("private:entrymsg");
264 logcommand (si, CMDLOG_SET, "%s SET ENTRYMSG NONE", mc->name, parv[1]);
265 command_success_nodata (si, _("The entry message for \2%s\2 has been cleared."), parv[0]);
266 return;
267 }
268
269 command_fail (si, fault::nochange, _("The entry message for \2%s\2 was not set."), parv[0]);
270 return;
271 }
272
273 /* we'll overwrite any existing metadata */
274 mc->add_metadata ("private:entrymsg", parv[1]);
275
276 logcommand (si, CMDLOG_SET, "%s SET ENTRYMSG %s", mc->name, parv[1]);
277 command_success_nodata (si, _("The entry message for \2%s\2 has been set to \2%s\2"), parv[0], parv[1]);
278 }
279
280 /*
281 * This is how CS SET FOUNDER behaves in the absence of channel passwords:
282 *
283 * To transfer a channel, the original founder (OF) issues the command:
284 * /CS SET #chan FOUNDER NF
285 * where NF is the new founder of the channel.
286 *
287 * Then, to complete the transfer, the NF must issue the command:
288 * /CS SET #chan FOUNDER NF
289 *
290 * To cancel the transfer before it completes, the OF can issue the command:
291 * /CS SET #chan FOUNDER OF
292 *
293 * The purpose of the confirmation step is to prevent users from giving away
294 * undesirable channels (e.g. registering #kidsex and transferring to an
295 * innocent user.) Originally, we used channel passwords for this purpose.
296 */
297 static void
298 cs_cmd_set_founder (sourceinfo_t *si, int parc, char *parv[])
299 {
300 char *newfounder = parv[1];
301 myuser_t *tmu;
302 mychan_t *mc;
303
304 if (!si->smu)
305 {
306 command_fail (si, fault::noprivs, _("You are not logged in."));
307 return;
308 }
309
310 if (!(tmu = myuser_t::find_ext (newfounder)))
311 {
312 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), newfounder);
313 return;
314 }
315
316 if (!(mc = mychan_t::find (parv[0])))
317 {
318 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), parv[0]);
319 return;
320 }
321
322 if (!si->smu->is_founder (mc))
323 {
324 /* User is not currently the founder.
325 * Maybe he is trying to complete a transfer?
326 */
327 metadata *md;
328
329 /* XXX is it portable to compare times like that? */
330 if ((si->smu == tmu) && (md = mc->find_metadata ("private:verify:founderchg:newfounder")) && !irccasecmp (md->value, si->smu->name) && (md = mc->find_metadata ("private:verify:founderchg:timestamp")) && (atol (md->value) >= si->smu->registered))
331 {
332 node_t *n;
333 chanacs_t *ca;
334
335 if ((tmu->num_channels () >= me.maxchans) && !has_priv_myuser (tmu, PRIV_REG_NOLIMIT))
336 {
337 command_fail (si, fault::toomany, _("\2%s\2 has too many channels registered."), tmu->name);
338 return;
339 }
340
341 if (mc->find_metadata ("private:close:closer"))
342 {
343 command_fail (si, fault::noprivs, _("\2%s\2 is closed; it cannot be transferred."), mc->name);
344 return;
345 }
346
347 logcommand (si, CMDLOG_REGISTER, "%s SET FOUNDER %s (completing transfer from %s)", mc->name, tmu->name, mc->founder_names ());
348 verbose (mc, "Foundership transferred from \2%s\2 to \2%s\2.", mc->founder_names (), tmu->name);
349
350 /* add target as founder... */
351 LIST_FOREACH (n, mc->chanacs.head)
352 {
353 ca = static_cast<chanacs_t *> (n->data);
354 /* CA_FLAGS is always on if CA_FOUNDER is on, this just
355 * ensures we don't crash if not -- jilles
356 */
357 if (ca->myuser != NULL && ca->level & CA_FOUNDER)
358 chanacs_modify_simple (ca, CA_FLAGS, CA_FOUNDER);
359 }
360 chanacs_change_simple (mc, tmu, NULL, CA_FOUNDER_0, 0);
361
362 /* delete transfer metadata */
363 mc->del_metadata ("private:verify:founderchg:newfounder");
364 mc->del_metadata ("private:verify:founderchg:timestamp");
365
366 /* done! */
367 snoop ("SET:FOUNDER: \2%s\2 -> \2%s\2", mc->name, tmu->name);
368 command_success_nodata (si, _("Transfer complete: \2%s\2 has been set as founder for \2%s\2."), tmu->name, mc->name);
369
370 return;
371 }
372
373 command_fail (si, fault::noprivs, _("You are not the founder of \2%s\2."), mc->name);
374 return;
375 }
376
377 if (tmu->is_founder (mc))
378 {
379 /* User is currently the founder and
380 * trying to transfer back to himself.
381 * Maybe he is trying to cancel a transfer?
382 */
383
384 if (mc->find_metadata ("private:verify:founderchg:newfounder"))
385 {
386 mc->del_metadata ("private:verify:founderchg:newfounder");
387 mc->del_metadata ("private:verify:founderchg:timestamp");
388
389 logcommand (si, CMDLOG_REGISTER, "%s SET FOUNDER %s (cancelling transfer)", mc->name, tmu->name);
390 command_success_nodata (si, _("The transfer of \2%s\2 has been cancelled."), mc->name);
391
392 return;
393 }
394
395 command_fail (si, fault::nochange, _("\2%s\2 is already the founder of \2%s\2."), tmu->name, mc->name);
396 return;
397 }
398
399 /* If the target user does not have access yet, this may overflow
400 * the access list. Check at this time because that is more convenient
401 * for users.
402 * -- jilles
403 */
404 if (!chanacs_find (mc, tmu, 0))
405 {
406 chanacs_t *ca;
407
408 ca = chanacs_open (mc, tmu, NULL, true);
409 if (ca->level == 0 && chanacs_is_table_full (ca))
410 {
411 command_fail (si, fault::toomany, _("Channel %s access list is full."), mc->name);
412 chanacs_close (ca);
413 return;
414 }
415 chanacs_close (ca);
416 }
417
418 /* check for lazy cancellation of outstanding requests */
419 if (mc->find_metadata ("private:verify:founderchg:newfounder"))
420 {
421 logcommand (si, CMDLOG_REGISTER, "%s SET FOUNDER %s (cancelling old transfer and initializing transfer)", mc->name, tmu->name);
422 command_success_nodata (si, _("The previous transfer request for \2%s\2 has been cancelled."), mc->name);
423 }
424 else
425 logcommand (si, CMDLOG_REGISTER, "%s SET FOUNDER %s (initializing transfer)", mc->name, tmu->name);
426
427 mc->add_metadata ("private:verify:founderchg:newfounder", tmu->name);
428 mc->add_metadata ("private:verify:founderchg:timestamp", itoa (NOW));
429
430 command_success_nodata (si, _("\2%s\2 can now take ownership of \2%s\2."), tmu->name, mc->name);
431 command_success_nodata (si, _("In order to complete the transfer, \2%s\2 must perform the following command:"), tmu->name);
432 command_success_nodata (si, " \2/msg %s SET %s FOUNDER %s\2", chansvs.nick, mc->name, tmu->name);
433 command_success_nodata (si, _("After that command is issued, the channel will be transferred."), mc->name);
434 command_success_nodata (si, _("To cancel the transfer, use \2/msg %s SET %s FOUNDER %s\2"), chansvs.nick, mc->name, si->smu->name);
435 }
436
437 static void
438 cs_cmd_set_mlock (sourceinfo_t *si, int parc, char *parv[])
439 {
440 mychan_t *mc;
441 char modebuf[32], *end, c;
442 int add = -1;
443 int newlock_on = 0, newlock_off = 0, newlock_limit = 0, flag = 0;
444 int mask;
445 char newlock_key[KEYLEN];
446 char newlock_ext[MAXEXTMODES][512];
447 bool newlock_ext_off[MAXEXTMODES];
448 char newext[512];
449 char ext_plus[MAXEXTMODES + 1], ext_minus[MAXEXTMODES + 1];
450 int i;
451 char *letters = strtok (parv[1], " ");
452 char *arg;
453
454 if (!letters)
455 {
456 command_fail (si, fault::badparams, STR_INVALID_PARAMS, "MLOCK");
457 return;
458 }
459
460 if (!(mc = mychan_t::find (parv[0])))
461 {
462 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), parv[0]);
463 return;
464 }
465
466 if (!chanacs_source_has_flag (mc, si, CA_SET))
467 {
468 command_fail (si, fault::noprivs, _("You are not authorized to perform this command."));
469 return;
470 }
471
472 for (i = 0; i < MAXEXTMODES; i++)
473 {
474 newlock_ext[i][0] = '\0';
475 newlock_ext_off[i] = false;
476 }
477 ext_plus[0] = '\0';
478 ext_minus[0] = '\0';
479 newlock_key[0] = '\0';
480
481 mask = has_priv (si, PRIV_CHAN_CMODES) ? 0 : ircd->oper_only_modes;
482
483 while (*letters)
484 {
485 if (*letters != '+' && *letters != '-' && add < 0)
486 {
487 letters++;
488 continue;
489 }
490
491 switch ((c = *letters++))
492 {
493 case '+':
494 add = 1;
495 break;
496
497 case '-':
498 add = 0;
499 break;
500
501 case 'k':
502 if (add)
503 {
504 arg = strtok (NULL, " ");
505 if (!arg)
506 {
507 command_fail (si, fault::badparams, _("You need to specify which key to MLOCK."));
508 return;
509 }
510 else if (strlen (arg) >= KEYLEN)
511 {
512 command_fail (si, fault::badparams, _("MLOCK key is too long (%d > %d)."), strlen (arg), KEYLEN - 1);
513 return;
514 }
515 else if (strchr (arg, ',') || arg[0] == ':')
516 {
517 command_fail (si, fault::badparams, _("MLOCK key contains invalid characters."));
518 return;
519 }
520
521 strlcpy (newlock_key, arg, sizeof newlock_key);
522 newlock_off &= ~CMODE_KEY;
523 }
524 else
525 {
526 newlock_key[0] = '\0';
527 newlock_off |= CMODE_KEY;
528 }
529
530 break;
531
532 case 'l':
533 if (add)
534 {
535 arg = strtok (NULL, " ");
536 if (!arg)
537 {
538 command_fail (si, fault::badparams, _("You need to specify what limit to MLOCK."));
539 return;
540 }
541
542 if (atol (arg) <= 0)
543 {
544 command_fail (si, fault::badparams, _("You must specify a positive integer for limit."));
545 return;
546 }
547
548 newlock_limit = atol (arg);
549 newlock_off &= ~CMODE_LIMIT;
550 }
551 else
552 {
553 newlock_limit = 0;
554 newlock_off |= CMODE_LIMIT;
555 }
556
557 break;
558
559 default:
560 flag = mode_to_flag (c);
561
562 if (flag)
563 {
564 if (add)
565 newlock_on |= flag, newlock_off &= ~flag;
566 else
567 newlock_off |= flag, newlock_on &= ~flag;
568 break;
569 }
570
571 for (i = 0; ignore_mode_list[i].mode != '\0'; i++)
572 {
573 if (c == ignore_mode_list[i].mode)
574 {
575 if (add)
576 {
577 arg = strtok (NULL, " ");
578 if (!arg)
579 {
580 command_fail (si, fault::badparams, _("You need to specify a value for mode +%c."), c);
581 return;
582 }
583 if (strlen (arg) > 350)
584 {
585 command_fail (si, fault::badparams, _("Invalid value \2%s\2 for mode +%c."), arg, c);
586 return;
587 }
588 if ((mc->chan == NULL || mc->chan->extmodes[i] == NULL || strcmp (mc->chan->extmodes[i], arg)) && !ignore_mode_list[i].check (arg, mc->chan, mc, si->su, si->smu))
589 {
590 command_fail (si, fault::badparams, _("Invalid value \2%s\2 for mode +%c."), arg, c);
591 return;
592 }
593 strlcpy (newlock_ext[i], arg, sizeof newlock_ext[i]);
594 newlock_ext_off[i] = false;
595 }
596 else
597 {
598 newlock_ext[i][0] = '\0';
599 newlock_ext_off[i] = true;
600 }
601 }
602 }
603 }
604 }
605
606 if (strlen (newext) > 450)
607 {
608 command_fail (si, fault::badparams, _("Mode lock is too long."));
609 return;
610 }
611
612 /* save it to mychan */
613 /* leave the modes in mask unchanged -- jilles */
614 mc->mlock_on = (newlock_on & ~mask) | (mc->mlock_on & mask);
615 mc->mlock_off = (newlock_off & ~mask) | (mc->mlock_off & mask);
616 mc->mlock_limit = newlock_limit;
617
618 if (mc->mlock_key)
619 sfree (mc->mlock_key);
620
621 mc->mlock_key = *newlock_key != '\0' ? sstrdup (newlock_key) : NULL;
622
623 newext[0] = '\0';
624 for (i = 0; i < MAXEXTMODES; i++)
625 {
626 if (newlock_ext[i][0] != '\0' || newlock_ext_off[i])
627 {
628 if (*newext != '\0')
629 {
630 modebuf[0] = ' ';
631 modebuf[1] = '\0';
632 strlcat (newext, modebuf, sizeof newext);
633 }
634 modebuf[0] = ignore_mode_list[i].mode;
635 modebuf[1] = '\0';
636 strlcat (newext, modebuf, sizeof newext);
637 strlcat (newlock_ext_off[i] ? ext_minus : ext_plus, modebuf, MAXEXTMODES + 1);
638 if (!newlock_ext_off[i])
639 strlcat (newext, newlock_ext[i], sizeof newext);
640 }
641 }
642 if (newext[0] != '\0')
643 mc->add_metadata ("private:mlockext", newext);
644 else
645 mc->del_metadata ("private:mlockext");
646
647 end = modebuf;
648 *end = 0;
649
650 if (mc->mlock_on || mc->mlock_key || mc->mlock_limit || *ext_plus)
651 end += snprintf (end, sizeof (modebuf) - (end - modebuf), "+%s%s%s%s", flags_to_string (mc->mlock_on), mc->mlock_key ? "k" : "", mc->mlock_limit ? "l" : "", ext_plus);
652
653 if (mc->mlock_off || *ext_minus)
654 end += snprintf (end, sizeof (modebuf) - (end - modebuf), "-%s%s%s%s", flags_to_string (mc->mlock_off), mc->mlock_off & CMODE_KEY ? "k" : "", mc->mlock_off & CMODE_LIMIT ? "l" : "", ext_minus);
655
656 if (*modebuf)
657 {
658 command_success_nodata (si, _("The MLOCK for \2%s\2 has been set to \2%s\2."), mc->name, modebuf);
659 logcommand (si, CMDLOG_SET, "%s SET MLOCK %s", mc->name, modebuf);
660 }
661 else
662 {
663 command_success_nodata (si, _("The MLOCK for \2%s\2 has been removed."), mc->name);
664 logcommand (si, CMDLOG_SET, "%s SET MLOCK NONE", mc->name);
665 }
666
667 check_modes (mc, true);
668
669 return;
670 }
671
672 static void
673 cs_cmd_set_keeptopic (sourceinfo_t *si, int parc, char *parv[])
674 {
675 mychan_t *mc;
676
677 if (!(mc = mychan_t::find (parv[0])))
678 {
679 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), parv[0]);
680 return;
681 }
682
683 if (!chanacs_source_has_flag (mc, si, CA_SET))
684 {
685 command_fail (si, fault::noprivs, _("You are not authorized to perform this command."));
686 return;
687 }
688
689 if (!strcasecmp ("ON", parv[1]))
690 {
691 if (MC_KEEPTOPIC & mc->flags)
692 {
693 command_fail (si, fault::nochange, _("The \2%s\2 flag is already set for \2%s\2."), "KEEPTOPIC", mc->name);
694 return;
695 }
696
697 logcommand (si, CMDLOG_SET, "%s SET KEEPTOPIC ON", mc->name);
698
699 mc->flags |= MC_KEEPTOPIC;
700
701 command_success_nodata (si, _("The \2%s\2 flag has been set for \2%s\2."), "KEEPTOPIC", mc->name);
702
703 return;
704 }
705
706 else if (!strcasecmp ("OFF", parv[1]))
707 {
708 if (!(MC_KEEPTOPIC & mc->flags))
709 {
710 command_fail (si, fault::nochange, _("The \2%s\2 flag is not set for \2%s\2."), "KEEPTOPIC", mc->name);
711 return;
712 }
713
714 logcommand (si, CMDLOG_SET, "%s SET KEEPTOPIC OFF", mc->name);
715
716 mc->flags &= ~(MC_KEEPTOPIC | MC_TOPICLOCK);
717
718 command_success_nodata (si, _("The \2%s\2 flag has been removed for \2%s\2."), "KEEPTOPIC", mc->name);
719
720 return;
721 }
722
723 else
724 {
725 command_fail (si, fault::badparams, STR_INVALID_PARAMS, "KEEPTOPIC");
726 return;
727 }
728 }
729
730 static void
731 cs_cmd_set_topiclock (sourceinfo_t *si, int parc, char *parv[])
732 {
733 mychan_t *mc;
734
735 if (!(mc = mychan_t::find (parv[0])))
736 {
737 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), parv[0]);
738 return;
739 }
740
741 if (!chanacs_source_has_flag (mc, si, CA_SET))
742 {
743 command_fail (si, fault::noprivs, _("You are not authorized to perform this command."));
744 return;
745 }
746
747 if (!strcasecmp ("ON", parv[1]))
748 {
749 if (MC_TOPICLOCK & mc->flags)
750 {
751 command_fail (si, fault::nochange, _("The \2%s\2 flag is already set for \2%s\2."), "TOPICLOCK", mc->name);
752 return;
753 }
754
755 logcommand (si, CMDLOG_SET, "%s SET TOPICLOCK ON", mc->name);
756
757 mc->flags |= MC_KEEPTOPIC | MC_TOPICLOCK;
758
759 command_success_nodata (si, _("The \2%s\2 flag has been set for \2%s\2."), "TOPICLOCK", mc->name);
760
761 return;
762 }
763
764 else if (!strcasecmp ("OFF", parv[1]))
765 {
766 if (!(MC_TOPICLOCK & mc->flags))
767 {
768 command_fail (si, fault::nochange, _("The \2%s\2 flag is not set for \2%s\2."), "TOPICLOCK", mc->name);
769 return;
770 }
771
772 logcommand (si, CMDLOG_SET, "%s SET TOPICLOCK OFF", mc->name);
773
774 mc->flags &= ~MC_TOPICLOCK;
775
776 command_success_nodata (si, _("The \2%s\2 flag has been removed for \2%s\2."), "TOPICLOCK", mc->name);
777
778 return;
779 }
780
781 else
782 {
783 command_fail (si, fault::badparams, STR_INVALID_PARAMS, "TOPICLOCK");
784 return;
785 }
786 }
787
788 static void
789 cs_cmd_set_secure (sourceinfo_t *si, int parc, char *parv[])
790 {
791 mychan_t *mc;
792
793 if (!(mc = mychan_t::find (parv[0])))
794 {
795 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), parv[0]);
796 return;
797 }
798
799 if (!chanacs_source_has_flag (mc, si, CA_SET))
800 {
801 command_fail (si, fault::noprivs, _("You are not authorized to perform this command."));
802 return;
803 }
804
805 if (!strcasecmp ("ON", parv[1]))
806 {
807 if (MC_SECURE & mc->flags)
808 {
809 command_fail (si, fault::nochange, _("The \2%s\2 flag is already set for \2%s\2."), "SECURE", mc->name);
810 return;
811 }
812
813 logcommand (si, CMDLOG_SET, "%s SET SECURE ON", mc->name);
814
815 mc->flags |= MC_SECURE;
816
817 command_success_nodata (si, _("The \2%s\2 flag has been set for \2%s\2."), "SECURE", mc->name);
818
819 return;
820 }
821
822 else if (!strcasecmp ("OFF", parv[1]))
823 {
824 if (!(MC_SECURE & mc->flags))
825 {
826 command_fail (si, fault::nochange, _("The \2%s\2 flag is not set for \2%s\2."), "SECURE", mc->name);
827 return;
828 }
829
830 logcommand (si, CMDLOG_SET, "%s SET SECURE OFF", mc->name);
831
832 mc->flags &= ~MC_SECURE;
833
834 command_success_nodata (si, _("The \2%s\2 flag has been removed for \2%s\2."), "SECURE", mc->name);
835
836 return;
837 }
838
839 else
840 {
841 command_fail (si, fault::badparams, STR_INVALID_PARAMS, "SECURE");
842 return;
843 }
844 }
845
846 static void
847 cs_cmd_set_verbose (sourceinfo_t *si, int parc, char *parv[])
848 {
849 mychan_t *mc;
850
851 if (!(mc = mychan_t::find (parv[0])))
852 {
853 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), parv[0]);
854 return;
855 }
856
857 if (!chanacs_source_has_flag (mc, si, CA_SET))
858 {
859 command_fail (si, fault::noprivs, _("You are not authorized to perform this command."));
860 return;
861 }
862
863 if (!strcasecmp ("ON", parv[1]) || !strcasecmp ("ALL", parv[1]))
864 {
865 if (MC_VERBOSE & mc->flags)
866 {
867 command_fail (si, fault::nochange, _("The \2%s\2 flag is already set for \2%s\2."), "VERBOSE", mc->name);
868 return;
869 }
870
871 logcommand (si, CMDLOG_SET, "%s SET VERBOSE ON", mc->name);
872
873 mc->flags &= ~MC_VERBOSE_OPS;
874 mc->flags |= MC_VERBOSE;
875
876 verbose (mc, "\2%s\2 enabled the VERBOSE flag", get_source_name (si));
877 command_success_nodata (si, _("The \2%s\2 flag has been set for \2%s\2."), "VERBOSE", mc->name);
878
879 return;
880 }
881
882 else if (!strcasecmp ("OPS", parv[1]))
883 {
884 if (MC_VERBOSE_OPS & mc->flags)
885 {
886 command_fail (si, fault::nochange, _("The \2%s\2 flag is already set for \2%s\2."), "VERBOSE_OPS", mc->name);
887 return;
888 }
889
890 logcommand (si, CMDLOG_SET, "%s SET VERBOSE OPS", mc->name);
891
892 if (mc->flags & MC_VERBOSE)
893 {
894 verbose (mc, "\2%s\2 restricted VERBOSE to chanops", get_source_name (si));
895 mc->flags &= ~MC_VERBOSE;
896 mc->flags |= MC_VERBOSE_OPS;
897 }
898 else
899 {
900 mc->flags |= MC_VERBOSE_OPS;
901 verbose (mc, "\2%s\2 enabled the VERBOSE_OPS flag", get_source_name (si));
902 }
903
904 command_success_nodata (si, _("The \2%s\2 flag has been set for \2%s\2."), "VERBOSE_OPS", mc->name);
905
906 return;
907 }
908 else if (!strcasecmp ("OFF", parv[1]))
909 {
910 if (!((MC_VERBOSE | MC_VERBOSE_OPS) & mc->flags))
911 {
912 command_fail (si, fault::nochange, _("The \2%s\2 flag is not set for \2%s\2."), "VERBOSE", mc->name);
913 return;
914 }
915
916 logcommand (si, CMDLOG_SET, "%s SET VERBOSE OFF", mc->name);
917
918 if (mc->flags & MC_VERBOSE)
919 verbose (mc, "\2%s\2 disabled the VERBOSE flag", get_source_name (si));
920 else
921 verbose (mc, "\2%s\2 disabled the VERBOSE_OPS flag", get_source_name (si));
922 mc->flags &= ~(MC_VERBOSE | MC_VERBOSE_OPS);
923
924 command_success_nodata (si, _("The \2%s\2 flag has been removed for \2%s\2."), "VERBOSE", mc->name);
925
926 return;
927 }
928
929 else
930 {
931 command_fail (si, fault::badparams, STR_INVALID_PARAMS, "VERBOSE");
932 return;
933 }
934 }
935
936 static void
937 cs_cmd_set_fantasy (sourceinfo_t *si, int parc, char *parv[])
938 {
939 mychan_t *mc;
940
941 if (!(mc = mychan_t::find (parv[0])))
942 {
943 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), parv[0]);
944 return;
945 }
946
947 if (!chanacs_source_has_flag (mc, si, CA_SET))
948 {
949 command_fail (si, fault::noprivs, _("You are not authorized to perform this command."));
950 return;
951 }
952
953 if (!strcasecmp ("ON", parv[1]))
954 {
955 metadata *md = mc->find_metadata ("disable_fantasy");
956
957 if (!md)
958 {
959 command_fail (si, fault::nochange, _("The \2%s\2 flag is already set for \2%s\2."), "FANTASY", mc->name);
960 return;
961 }
962
963 mc->del_metadata ("disable_fantasy");
964
965 logcommand (si, CMDLOG_SET, "%s SET FANTASY ON", mc->name);
966 command_success_nodata (si, _("The \2%s\2 flag has been set for \2%s\2."), "FANTASY", mc->name);
967 return;
968 }
969 else if (!strcasecmp ("OFF", parv[1]))
970 {
971 metadata *md = mc->find_metadata ("disable_fantasy");
972
973 if (md)
974 {
975 command_fail (si, fault::nochange, _("The \2%s\2 flag is not set for \2%s\2."), "FANTASY", mc->name);
976 return;
977 }
978
979 mc->add_metadata ("disable_fantasy", "on");
980
981 logcommand (si, CMDLOG_SET, "%s SET FANTASY OFF", mc->name);
982 command_success_nodata (si, _("The \2%s\2 flag has been removed for \2%s\2."), "FANTASY", mc->name);
983 return;
984 }
985
986 else
987 {
988 command_fail (si, fault::badparams, STR_INVALID_PARAMS, "FANTASY");
989 return;
990 }
991 }
992
993 static void
994 cs_cmd_set_guard (sourceinfo_t *si, int parc, char *parv[])
995 {
996 mychan_t *mc;
997
998 if (!(mc = mychan_t::find (parv[0])))
999 {
1000 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), parv[0]);
1001 return;
1002 }
1003
1004 if (!chanacs_source_has_flag (mc, si, CA_SET))
1005 {
1006 command_fail (si, fault::noprivs, _("You are not authorized to perform this command."));
1007 return;
1008 }
1009
1010 if (!strcasecmp ("ON", parv[1]))
1011 {
1012 if (MC_GUARD & mc->flags)
1013 {
1014 command_fail (si, fault::nochange, _("The \2%s\2 flag is already set for \2%s\2."), "GUARD", mc->name);
1015 return;
1016 }
1017
1018 logcommand (si, CMDLOG_SET, "%s SET GUARD ON", mc->name);
1019
1020 mc->flags |= MC_GUARD;
1021
1022 if (!(mc->flags & MC_INHABIT))
1023 join (mc->name, chansvs.nick);
1024
1025 command_success_nodata (si, _("The \2%s\2 flag has been set for \2%s\2."), "GUARD", mc->name);
1026
1027 return;
1028 }
1029
1030 else if (!strcasecmp ("OFF", parv[1]))
1031 {
1032 if (!(MC_GUARD & mc->flags))
1033 {
1034 command_fail (si, fault::nochange, _("The \2%s\2 flag is not set for \2%s\2."), "GUARD", mc->name);
1035 return;
1036 }
1037
1038 logcommand (si, CMDLOG_SET, "%s SET GUARD OFF", mc->name);
1039
1040 mc->flags &= ~MC_GUARD;
1041
1042 if (!(mc->flags & MC_INHABIT) && (!config_options.chan || irccasecmp (config_options.chan, mc->name)))
1043 part (mc->name, chansvs.nick);
1044
1045 command_success_nodata (si, _("The \2%s\2 flag has been removed for \2%s\2."), "GUARD", mc->name);
1046
1047 return;
1048 }
1049
1050 else
1051 {
1052 command_fail (si, fault::badparams, STR_INVALID_PARAMS, "GUARD");
1053 return;
1054 }
1055 }
1056
1057 static void
1058 cs_cmd_set_staffonly (sourceinfo_t *si, int parc, char *parv[])
1059 {
1060 mychan_t *mc;
1061
1062 if (!(mc = mychan_t::find (parv[0])))
1063 {
1064 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), parv[0]);
1065 return;
1066 }
1067
1068 if (!strcasecmp ("ON", parv[1]))
1069 {
1070 if (MC_STAFFONLY & mc->flags)
1071 {
1072 command_fail (si, fault::nochange, _("The \2%s\2 flag is already set for \2%s\2."), "STAFFONLY", mc->name);
1073 return;
1074 }
1075
1076 snoop ("SET:STAFFONLY:ON: for \2%s\2 by \2%s\2", mc->name, get_oper_name (si));
1077 logcommand (si, CMDLOG_SET, "%s SET STAFFONLY ON", mc->name);
1078
1079 mc->flags |= MC_STAFFONLY;
1080
1081 command_success_nodata (si, _("The \2%s\2 flag has been set for \2%s\2."), "STAFFONLY", mc->name);
1082
1083 return;
1084 }
1085
1086 else if (!strcasecmp ("OFF", parv[1]))
1087 {
1088 if (!(MC_STAFFONLY & mc->flags))
1089 {
1090 command_fail (si, fault::nochange, _("The \2%s\2 flag is not set for \2%s\2."), "STAFFONLY", mc->name);
1091 return;
1092 }
1093
1094 snoop ("SET:STAFFONLY:OFF: for \2%s\2 by \2%s\2", mc->name, get_oper_name (si));
1095 logcommand (si, CMDLOG_SET, "%s SET STAFFONLY OFF", mc->name);
1096
1097 mc->flags &= ~MC_STAFFONLY;
1098
1099 command_success_nodata (si, _("The \2%s\2 flag has been removed for \2%s\2."), "STAFFONLY", mc->name);
1100
1101 return;
1102 }
1103
1104 else
1105 {
1106 command_fail (si, fault::badparams, STR_INVALID_PARAMS, "STAFFONLY");
1107 return;
1108 }
1109 }
1110
1111 static void
1112 cs_cmd_set_property (sourceinfo_t *si, int parc, char *parv[])
1113 {
1114 mychan_t *mc;
1115 char *property = strtok (parv[1], " ");
1116 char *value = strtok (NULL, "");
1117
1118 if (!property)
1119 {
1120 command_fail (si, fault::needmoreparams, _("Syntax: SET <#channel> PROPERTY <property> [value]"));
1121 return;
1122 }
1123
1124 /* do we really need to allow this? -- jilles */
1125 if (strchr (property, ':') && !has_priv (si, PRIV_METADATA))
1126 {
1127 command_fail (si, fault::badparams, _("Invalid property name."));
1128 return;
1129 }
1130
1131 if (!(mc = mychan_t::find (parv[0])))
1132 {
1133 command_fail (si, fault::nosuch_target, _("\2%s\2 is not registered."), parv[0]);
1134 return;
1135 }
1136
1137 if (!si->smu->is_founder (mc))
1138 {
1139 command_fail (si, fault::noprivs, _("You are not authorized to perform this command."));
1140 return;
1141 }
1142
1143 if (mc->metadata.count >= me.mdlimit)
1144 {
1145 command_fail (si, fault::toomany, _("Cannot add \2%s\2 to \2%s\2 metadata table, it is full."), property, parv[0]);
1146 return;
1147 }
1148
1149 if (strchr (property, ':'))
1150 snoop ("SET:PROPERTY: \2%s\2: \2%s\2/\2%s\2", mc->name, property, value);
1151
1152 if (!value)
1153 {
1154 metadata *md = mc->find_metadata (property);
1155
1156 if (!md)
1157 {
1158 command_fail (si, fault::nochange, _("Metadata entry \2%s\2 was not set."), property);
1159 return;
1160 }
1161
1162 mc->del_metadata (property);
1163 logcommand (si, CMDLOG_SET, "%s SET PROPERTY %s (deleted)", mc->name, property);
1164 command_success_nodata (si, _("Metadata entry \2%s\2 has been deleted."), property);
1165 return;
1166 }
1167
1168 if (strlen (property) > 32 || strlen (value) > 300)
1169 {
1170 command_fail (si, fault::badparams, _("Parameters are too long. Aborting."));
1171 return;
1172 }
1173
1174 mc->add_metadata (property, value);
1175 logcommand (si, CMDLOG_SET, "%s SET PROPERTY %s to %s", mc->name, property, value);
1176 command_success_nodata (si, _("Metadata entry \2%s\2 added."), property);
1177 }
1178
1179 bool
1180 _modinit (module *m)
1181 {
1182 cs_cmdtree << cs_set;
1183 cs_set_cmdtree << cs_set_commands;
1184
1185 help_addentry (cs_helptree, "SET", NULL, cs_help_set);
1186 help_addentry (cs_helptree, "SET FOUNDER", "help/chanserv/set_founder", NULL);
1187 help_addentry (cs_helptree, "SET MLOCK", "help/chanserv/set_mlock", NULL);
1188 help_addentry (cs_helptree, "SET SECURE", "help/chanserv/set_secure", NULL);
1189 help_addentry (cs_helptree, "SET VERBOSE", "help/chanserv/set_verbose", NULL);
1190 help_addentry (cs_helptree, "SET URL", "help/chanserv/set_url", NULL);
1191 help_addentry (cs_helptree, "SET EMAIL", "help/chanserv/set_email", NULL);
1192 help_addentry (cs_helptree, "SET ENTRYMSG", "help/chanserv/set_entrymsg", NULL);
1193 help_addentry (cs_helptree, "SET PROPERTY", "help/chanserv/set_property", NULL);
1194 help_addentry (cs_helptree, "SET STAFFONLY", "help/chanserv/set_staffonly", NULL);
1195 help_addentry (cs_helptree, "SET KEEPTOPIC", "help/chanserv/set_keeptopic", NULL);
1196 help_addentry (cs_helptree, "SET TOPICLOCK", "help/chanserv/set_topiclock", NULL);
1197 help_addentry (cs_helptree, "SET FANTASY", "help/chanserv/set_fantasy", NULL);
1198 help_addentry (cs_helptree, "SET GUARD", "help/chanserv/set_guard", NULL);
1199
1200 ConfTable::callback.ready.attach (on_config_ready);
1201
1202 return true;
1203 }
1204
1205 void
1206 _moddeinit ()
1207 {
1208 cs_cmdtree >> cs_set;
1209 cs_set_cmdtree >> cs_set_commands;
1210
1211 help_delentry (cs_helptree, "SET");
1212 help_delentry (cs_helptree, "SET FOUNDER");
1213 help_delentry (cs_helptree, "SET MLOCK");
1214 help_delentry (cs_helptree, "SET SECURE");
1215 help_delentry (cs_helptree, "SET VERBOSE");
1216 help_delentry (cs_helptree, "SET URL");
1217 help_delentry (cs_helptree, "SET EMAIL");
1218 help_delentry (cs_helptree, "SET ENTRYMSG");
1219 help_delentry (cs_helptree, "SET PROPERTY");
1220 help_delentry (cs_helptree, "SET STAFFONLY");
1221 help_delentry (cs_helptree, "SET KEEPTOPIC");
1222 help_delentry (cs_helptree, "SET TOPICLOCK");
1223 help_delentry (cs_helptree, "SET FANTASY");
1224 help_delentry (cs_helptree, "SET GUARD");
1225
1226 ConfTable::callback.ready.detach (on_config_ready);
1227 }