ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/chanserv/set.C
Revision: 1.6
Committed: Tue Aug 28 22:18:30 2007 UTC (19 years, 1 month ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.5: +4 -4 lines
Log Message:
added type traits

File Contents

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