ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/chanserv/set.C
Revision: 1.2
Committed: Sat Jul 21 01:29:08 2007 UTC (19 years, 2 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.1: +2 -2 lines
Log Message:
- moved to new documentation system
- fixed small build error

File Contents

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