ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/chanserv/set.C
Revision: 1.9
Committed: Sun Sep 9 20:05:51 2007 UTC (19 years, 1 month ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.8: +3 -2 lines
Log Message:
- changed configurations to the c++ stdlib
- more #defines to enum
- removed getopt.h and link.h from the system as they were unused
- reworked logstreams
- added an itoa with old syntax
- made klines objects
- moved some global variables into appropriate classes
- fixed boost.foreach's compiler workaround #if's
- allow other files to add exceptions with ADD_EXCEPTION
- changed mynick_t to c++ object
- moved servers.h out of atheme.h
- corrected PING from inspircd 1.2

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