1 /* ChanServ core functions
2 *
3 * (C) 2003-2020 Anope Team
4 * Contact us at team@anope.org
5 *
6 * Please read COPYING and README for further details.
7 *
8 * Based on the original code of Epona by Lara.
9 * Based on the original code of Services by Andy Church.
10 */
11
12 #include "module.h"
13 #include "modules/cs_mode.h"
14
15 class CommandCSSet : public Command
16 {
17 public:
CommandCSSet(Module * creator)18 CommandCSSet(Module *creator) : Command(creator, "chanserv/set", 2, 3)
19 {
20 this->SetDesc(_("Set channel options and information"));
21 this->SetSyntax(_("\037option\037 \037channel\037 \037parameters\037"));
22 }
23
Execute(CommandSource & source,const std::vector<Anope::string> & params)24 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
25 {
26 this->OnSyntaxError(source, "");
27 }
28
OnHelp(CommandSource & source,const Anope::string & subcommand)29 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
30 {
31 this->SendSyntax(source);
32 source.Reply(" ");
33 source.Reply(_("Allows the channel founder to set various channel options\n"
34 "and other information.\n"
35 " \n"
36 "Available options:"));
37 Anope::string this_name = source.command;
38 bool hide_privileged_commands = Config->GetBlock("options")->Get<bool>("hideprivilegedcommands"),
39 hide_registered_commands = Config->GetBlock("options")->Get<bool>("hideregisteredcommands");
40 for (CommandInfo::map::const_iterator it = source.service->commands.begin(), it_end = source.service->commands.end(); it != it_end; ++it)
41 {
42 const Anope::string &c_name = it->first;
43 const CommandInfo &info = it->second;
44 if (c_name.find_ci(this_name + " ") == 0)
45 {
46 if (info.hide)
47 continue;
48
49 ServiceReference<Command> c("Command", info.name);
50
51 // XXX dup
52 if (!c)
53 continue;
54 else if (hide_registered_commands && !c->AllowUnregistered() && !source.GetAccount())
55 continue;
56 else if (hide_privileged_commands && !info.permission.empty() && !source.HasCommand(info.permission))
57 continue;
58
59 source.command = it->first;
60 c->OnServHelp(source);
61 }
62 }
63 source.Reply(_("Type \002%s%s HELP %s \037option\037\002 for more information on a\n"
64 "particular option."), Config->StrictPrivmsg.c_str(), source.service->nick.c_str(), this_name.c_str());
65 return true;
66 }
67 };
68
69 class CommandCSSetAutoOp : public Command
70 {
71 public:
CommandCSSetAutoOp(Module * creator,const Anope::string & cname="chanserv/set/autoop")72 CommandCSSetAutoOp(Module *creator, const Anope::string &cname = "chanserv/set/autoop") : Command(creator, cname, 2, 2)
73 {
74 this->SetDesc(_("Should services automatically give status to users"));
75 this->SetSyntax(_("\037channel\037 {ON | OFF}"));
76 }
77
Execute(CommandSource & source,const std::vector<Anope::string> & params)78 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
79 {
80 if (Anope::ReadOnly)
81 {
82 source.Reply(READ_ONLY_MODE);
83 return;
84 }
85
86 ChannelInfo *ci = ChannelInfo::Find(params[0]);
87 if (ci == NULL)
88 {
89 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
90 return;
91 }
92
93 EventReturn MOD_RESULT;
94 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
95 if (MOD_RESULT == EVENT_STOP)
96 return;
97
98 if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
99 {
100 source.Reply(ACCESS_DENIED);
101 return;
102 }
103
104 if (params[1].equals_ci("ON"))
105 {
106 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable autoop";
107 ci->Shrink<bool>("NOAUTOOP");
108 source.Reply(_("Services will now automatically give modes to users in \002%s\002."), ci->name.c_str());
109 }
110 else if (params[1].equals_ci("OFF"))
111 {
112 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable autoop";
113 ci->Extend<bool>("NOAUTOOP");
114 source.Reply(_("Services will no longer automatically give modes to users in \002%s\002."), ci->name.c_str());
115 }
116 else
117 this->OnSyntaxError(source, "AUTOOP");
118 }
119
OnHelp(CommandSource & source,const Anope::string &)120 bool OnHelp(CommandSource &source, const Anope::string &) anope_override
121 {
122 this->SendSyntax(source);
123 source.Reply(" ");
124 source.Reply(_("Enables or disables %s's autoop feature for a\n"
125 "channel. When disabled, users who join the channel will\n"
126 "not automatically gain any status from %s."), source.service->nick.c_str(),
127 source.service->nick.c_str(), this->name.c_str());
128 return true;
129 }
130 };
131
132 class CommandCSSetBanType : public Command
133 {
134 public:
CommandCSSetBanType(Module * creator,const Anope::string & cname="chanserv/set/bantype")135 CommandCSSetBanType(Module *creator, const Anope::string &cname = "chanserv/set/bantype") : Command(creator, cname, 2, 2)
136 {
137 this->SetDesc(_("Set how Services make bans on the channel"));
138 this->SetSyntax(_("\037channel\037 \037bantype\037"));
139 }
140
Execute(CommandSource & source,const std::vector<Anope::string> & params)141 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
142 {
143 if (Anope::ReadOnly)
144 {
145 source.Reply(READ_ONLY_MODE);
146 return;
147 }
148
149 ChannelInfo *ci = ChannelInfo::Find(params[0]);
150 if (ci == NULL)
151 {
152 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
153 return;
154 }
155
156 EventReturn MOD_RESULT;
157 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
158 if (MOD_RESULT == EVENT_STOP)
159 return;
160
161 if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
162 {
163 source.Reply(ACCESS_DENIED);
164 return;
165 }
166
167 try
168 {
169 int16_t new_type = convertTo<int16_t>(params[1]);
170 if (new_type < 0 || new_type > 3)
171 throw ConvertException("Invalid range");
172 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the ban type to " << new_type;
173 ci->bantype = new_type;
174 source.Reply(_("Ban type for channel %s is now #%d."), ci->name.c_str(), ci->bantype);
175 }
176 catch (const ConvertException &)
177 {
178 source.Reply(_("\002%s\002 is not a valid ban type."), params[1].c_str());
179 }
180 }
181
OnHelp(CommandSource & source,const Anope::string &)182 bool OnHelp(CommandSource &source, const Anope::string &) anope_override
183 {
184 this->SendSyntax(source);
185 source.Reply(" ");
186 source.Reply(_("Sets the ban type that will be used by services whenever\n"
187 "they need to ban someone from your channel.\n"
188 " \n"
189 "Bantype is a number between 0 and 3 that means:\n"
190 " \n"
191 "0: ban in the form *!user@host\n"
192 "1: ban in the form *!*user@host\n"
193 "2: ban in the form *!*@host\n"
194 "3: ban in the form *!*user@*.domain"), this->name.c_str());
195 return true;
196 }
197 };
198
199 class CommandCSSetDescription : public Command
200 {
201 public:
CommandCSSetDescription(Module * creator,const Anope::string & cname="chanserv/set/description")202 CommandCSSetDescription(Module *creator, const Anope::string &cname = "chanserv/set/description") : Command(creator, cname, 1, 2)
203 {
204 this->SetDesc(_("Set the channel description"));
205 this->SetSyntax(_("\037channel\037 [\037description\037]"));
206 }
207
Execute(CommandSource & source,const std::vector<Anope::string> & params)208 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
209 {
210 if (Anope::ReadOnly)
211 {
212 source.Reply(READ_ONLY_MODE);
213 return;
214 }
215
216 ChannelInfo *ci = ChannelInfo::Find(params[0]);
217 const Anope::string ¶m = params.size() > 1 ? params[1] : "";
218 if (ci == NULL)
219 {
220 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
221 return;
222 }
223
224 EventReturn MOD_RESULT;
225 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, param));
226 if (MOD_RESULT == EVENT_STOP)
227 return;
228
229 if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
230 {
231 source.Reply(ACCESS_DENIED);
232 return;
233 }
234
235 if (!param.empty())
236 {
237 ci->desc = param;
238 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the description to " << ci->desc;
239 source.Reply(_("Description of %s changed to \002%s\002."), ci->name.c_str(), ci->desc.c_str());
240 }
241 else
242 {
243 ci->desc.clear();
244 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to unset the description";
245 source.Reply(_("Description of %s unset."), ci->name.c_str());
246 }
247
248 return;
249 }
250
OnHelp(CommandSource & source,const Anope::string &)251 bool OnHelp(CommandSource &source, const Anope::string &) anope_override
252 {
253 this->SendSyntax(source);
254 source.Reply(" ");
255 source.Reply(_("Sets the description for the channel, which shows up with\n"
256 "the \002LIST\002 and \002INFO\002 commands."), this->name.c_str());
257 return true;
258 }
259 };
260
261 class CommandCSSetFounder : public Command
262 {
263 public:
CommandCSSetFounder(Module * creator,const Anope::string & cname="chanserv/set/founder")264 CommandCSSetFounder(Module *creator, const Anope::string &cname = "chanserv/set/founder") : Command(creator, cname, 2, 2)
265 {
266 this->SetDesc(_("Set the founder of a channel"));
267 this->SetSyntax(_("\037channel\037 \037nick\037"));
268 }
269
Execute(CommandSource & source,const std::vector<Anope::string> & params)270 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
271 {
272 if (Anope::ReadOnly)
273 {
274 source.Reply(READ_ONLY_MODE);
275 return;
276 }
277
278 ChannelInfo *ci = ChannelInfo::Find(params[0]);
279 if (ci == NULL)
280 {
281 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
282 return;
283 }
284
285 EventReturn MOD_RESULT;
286 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
287 if (MOD_RESULT == EVENT_STOP)
288 return;
289
290 if (MOD_RESULT != EVENT_ALLOW && (ci->HasExt("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && source.permission.empty() && !source.HasPriv("chanserv/administration"))
291 {
292 source.Reply(ACCESS_DENIED);
293 return;
294 }
295
296 const NickAlias *na = NickAlias::Find(params[1]);
297 if (!na)
298 {
299 source.Reply(NICK_X_NOT_REGISTERED, params[1].c_str());
300 return;
301 }
302
303 NickCore *nc = na->nc;
304 unsigned max_reg = Config->GetModule("chanserv")->Get<unsigned>("maxregistered");
305 if (max_reg && nc->channelcount >= max_reg && !source.HasPriv("chanserv/no-register-limit"))
306 {
307 source.Reply(_("\002%s\002 has too many channels registered."), na->nick.c_str());
308 return;
309 }
310
311 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the founder from " << (ci->GetFounder() ? ci->GetFounder()->display : "(none)") << " to " << nc->display;
312
313 ci->SetFounder(nc);
314
315 source.Reply(_("Founder of \002%s\002 changed to \002%s\002."), ci->name.c_str(), na->nick.c_str());
316
317 return;
318 }
319
OnHelp(CommandSource & source,const Anope::string &)320 bool OnHelp(CommandSource &source, const Anope::string &) anope_override
321 {
322 this->SendSyntax(source);
323 source.Reply(" ");
324 source.Reply(_("Changes the founder of a channel. The new nickname must\n"
325 "be a registered one."), this->name.c_str());
326 return true;
327 }
328 };
329
330 class CommandCSSetKeepModes : public Command
331 {
332 public:
CommandCSSetKeepModes(Module * creator,const Anope::string & cname="chanserv/set/keepmodes")333 CommandCSSetKeepModes(Module *creator, const Anope::string &cname = "chanserv/set/keepmodes") : Command(creator, cname, 2, 2)
334 {
335 this->SetDesc(_("Retain modes when channel is not in use"));
336 this->SetSyntax(_("\037channel\037 {ON | OFF}"));
337 }
338
Execute(CommandSource & source,const std::vector<Anope::string> & params)339 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
340 {
341 if (Anope::ReadOnly)
342 {
343 source.Reply(READ_ONLY_MODE);
344 return;
345 }
346
347 ChannelInfo *ci = ChannelInfo::Find(params[0]);
348 if (ci == NULL)
349 {
350 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
351 return;
352 }
353
354 EventReturn MOD_RESULT;
355 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
356 if (MOD_RESULT == EVENT_STOP)
357 return;
358
359 if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
360 {
361 source.Reply(ACCESS_DENIED);
362 return;
363 }
364
365 if (params[1].equals_ci("ON"))
366 {
367 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable keep modes";
368 ci->Extend<bool>("CS_KEEP_MODES");
369 source.Reply(_("Keep modes for %s is now \002on\002."), ci->name.c_str());
370 if (ci->c)
371 ci->last_modes = ci->c->GetModes();
372 }
373 else if (params[1].equals_ci("OFF"))
374 {
375 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable keep modes";
376 ci->Shrink<bool>("CS_KEEP_MODES");
377 source.Reply(_("Keep modes for %s is now \002off\002."), ci->name.c_str());
378 ci->last_modes.clear();
379 }
380 else
381 this->OnSyntaxError(source, "KEEPMODES");
382 }
383
OnHelp(CommandSource & source,const Anope::string &)384 bool OnHelp(CommandSource &source, const Anope::string &) anope_override
385 {
386 this->SendSyntax(source);
387 source.Reply(" ");
388 source.Reply(_("Enables or disables keepmodes for the given channel. If keep\n"
389 "modes is enabled, services will remember modes set on the channel\n"
390 "and attempt to re-set them the next time the channel is created."));
391 return true;
392 }
393 };
394
395 class CommandCSSetPeace : public Command
396 {
397 public:
CommandCSSetPeace(Module * creator,const Anope::string & cname="chanserv/set/peace")398 CommandCSSetPeace(Module *creator, const Anope::string &cname = "chanserv/set/peace") : Command(creator, cname, 2, 2)
399 {
400 this->SetDesc(_("Regulate the use of critical commands"));
401 this->SetSyntax(_("\037channel\037 {ON | OFF}"));
402 }
403
Execute(CommandSource & source,const std::vector<Anope::string> & params)404 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
405 {
406 if (Anope::ReadOnly)
407 {
408 source.Reply(READ_ONLY_MODE);
409 return;
410 }
411
412 ChannelInfo *ci = ChannelInfo::Find(params[0]);
413 if (ci == NULL)
414 {
415 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
416 return;
417 }
418
419 EventReturn MOD_RESULT;
420 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
421 if (MOD_RESULT == EVENT_STOP)
422 return;
423
424 if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
425 {
426 source.Reply(ACCESS_DENIED);
427 return;
428 }
429
430 if (params[1].equals_ci("ON"))
431 {
432 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable peace";
433 ci->Extend<bool>("PEACE");
434 source.Reply(_("Peace option for %s is now \002on\002."), ci->name.c_str());
435 }
436 else if (params[1].equals_ci("OFF"))
437 {
438 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable peace";
439 ci->Shrink<bool>("PEACE");
440 source.Reply(_("Peace option for %s is now \002off\002."), ci->name.c_str());
441 }
442 else
443 this->OnSyntaxError(source, "PEACE");
444
445 return;
446 }
447
OnHelp(CommandSource & source,const Anope::string &)448 bool OnHelp(CommandSource &source, const Anope::string &) anope_override
449 {
450 this->SendSyntax(source);
451 source.Reply(" ");
452 source.Reply(_("Enables or disables the \002peace\002 option for a channel.\n"
453 "When \002peace\002 is set, a user won't be able to kick,\n"
454 "ban or remove a channel status of a user that has\n"
455 "a level superior or equal to his via %s commands."), source.service->nick.c_str());
456 return true;
457 }
458 };
459
BotModes()460 inline static Anope::string BotModes()
461 {
462 return Config->GetModule("botserv")->Get<Anope::string>("botmodes",
463 Config->GetModule("chanserv")->Get<Anope::string>("botmodes", "o")
464 );
465 }
466
467 class CommandCSSetPersist : public Command
468 {
469 public:
CommandCSSetPersist(Module * creator,const Anope::string & cname="chanserv/set/persist")470 CommandCSSetPersist(Module *creator, const Anope::string &cname = "chanserv/set/persist") : Command(creator, cname, 2, 2)
471 {
472 this->SetDesc(_("Set the channel as permanent"));
473 this->SetSyntax(_("\037channel\037 {ON | OFF}"));
474 }
475
Execute(CommandSource & source,const std::vector<Anope::string> & params)476 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
477 {
478 if (Anope::ReadOnly)
479 {
480 source.Reply(READ_ONLY_MODE);
481 return;
482 }
483
484 ChannelInfo *ci = ChannelInfo::Find(params[0]);
485 if (ci == NULL)
486 {
487 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
488 return;
489 }
490
491 EventReturn MOD_RESULT;
492 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
493 if (MOD_RESULT == EVENT_STOP)
494 return;
495
496 if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
497 {
498 source.Reply(ACCESS_DENIED);
499 return;
500 }
501
502 ChannelMode *cm = ModeManager::FindChannelModeByName("PERM");
503
504 if (params[1].equals_ci("ON"))
505 {
506 if (!ci->HasExt("PERSIST"))
507 {
508 ci->Extend<bool>("PERSIST");
509
510 /* Set the perm mode */
511 if (cm)
512 {
513 if (ci->c && !ci->c->HasMode("PERM"))
514 ci->c->SetMode(NULL, cm);
515 /* Add it to the channels mlock */
516 ModeLocks *ml = ci->Require<ModeLocks>("modelocks");
517 if (ml)
518 ml->SetMLock(cm, true, "", source.GetNick());
519 }
520 /* No botserv bot, no channel mode, give them ChanServ.
521 * Yes, this works fine with no BotServ.
522 */
523 else if (!ci->bi)
524 {
525 BotInfo *ChanServ = Config->GetClient("ChanServ");
526 if (!ChanServ)
527 {
528 source.Reply(_("ChanServ is required to enable persist on this network."));
529 return;
530 }
531
532 ChanServ->Assign(NULL, ci);
533 if (ci->c && !ci->c->FindUser(ChanServ))
534 {
535 ChannelStatus status(BotModes());
536 ChanServ->Join(ci->c, &status);
537 }
538 }
539 }
540
541 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable persist";
542 source.Reply(_("Channel \002%s\002 is now persistent."), ci->name.c_str());
543 }
544 else if (params[1].equals_ci("OFF"))
545 {
546 if (ci->HasExt("PERSIST"))
547 {
548 ci->Shrink<bool>("PERSIST");
549
550 BotInfo *ChanServ = Config->GetClient("ChanServ"),
551 *BotServ = Config->GetClient("BotServ");
552
553 /* Unset perm mode */
554 if (cm)
555 {
556 if (ci->c && ci->c->HasMode("PERM"))
557 ci->c->RemoveMode(NULL, cm);
558 /* Remove from mlock */
559 ModeLocks *ml = ci->GetExt<ModeLocks>("modelocks");
560 if (ml)
561 ml->RemoveMLock(cm, true);
562 }
563 /* No channel mode, no BotServ, but using ChanServ as the botserv bot
564 * which was assigned when persist was set on
565 */
566 else if (!cm && !BotServ && ci->bi)
567 {
568 if (!ChanServ)
569 {
570 source.Reply(_("ChanServ is required to enable persist on this network."));
571 return;
572 }
573
574 /* Unassign bot */
575 ChanServ->UnAssign(NULL, ci);
576 }
577 }
578
579 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable persist";
580 source.Reply(_("Channel \002%s\002 is no longer persistent."), ci->name.c_str());
581 }
582 else
583 this->OnSyntaxError(source, "PERSIST");
584 }
585
OnHelp(CommandSource & source,const Anope::string &)586 bool OnHelp(CommandSource &source, const Anope::string &) anope_override
587 {
588 BotInfo *BotServ = Config->GetClient("BotServ");
589 BotInfo *ChanServ = Config->GetClient("ChanServ");
590 this->SendSyntax(source);
591 source.Reply(" ");
592 source.Reply(_("Enables or disables the persistent channel setting.\n"
593 "When persistent is set, the service bot will remain\n"
594 "in the channel when it has emptied of users.\n"
595 " \n"
596 "If your IRCd does not have a permanent (persistent) channel\n"
597 "mode you must have a service bot in your channel to\n"
598 "set persist on, and it can not be unassigned while persist\n"
599 "is on.\n"
600 " \n"
601 "If this network does not have %s enabled and does\n"
602 "not have a permanent channel mode, %s will\n"
603 "join your channel when you set persist on (and leave when\n"
604 "it has been set off).\n"
605 " \n"
606 "If your IRCd has a permanent (persistent) channel mode\n"
607 "and it is set or unset (for any reason, including MODE LOCK),\n"
608 "persist is automatically set and unset for the channel as well.\n"
609 "Additionally, services will set or unset this mode when you\n"
610 "set persist on or off."), BotServ ? BotServ->nick.c_str() : "BotServ",
611 ChanServ ? ChanServ->nick.c_str() : "ChanServ");
612 return true;
613 }
614 };
615
616 class CommandCSSetRestricted : public Command
617 {
618 public:
CommandCSSetRestricted(Module * creator,const Anope::string & cname="chanserv/set/restricted")619 CommandCSSetRestricted(Module *creator, const Anope::string &cname = "chanserv/set/restricted") : Command(creator, cname, 2, 2)
620 {
621 this->SetDesc(_("Restrict access to the channel"));
622 this->SetSyntax(_("\037channel\037 {ON | OFF}"));
623 }
624
Execute(CommandSource & source,const std::vector<Anope::string> & params)625 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
626 {
627 if (Anope::ReadOnly)
628 {
629 source.Reply(READ_ONLY_MODE);
630 return;
631 }
632
633 ChannelInfo *ci = ChannelInfo::Find(params[0]);
634 if (ci == NULL)
635 {
636 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
637 return;
638 }
639
640 EventReturn MOD_RESULT;
641 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
642 if (MOD_RESULT == EVENT_STOP)
643 return;
644
645 if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
646 {
647 source.Reply(ACCESS_DENIED);
648 return;
649 }
650
651 if (params[1].equals_ci("ON"))
652 {
653 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable restricted";
654 ci->Extend<bool>("RESTRICTED");
655 source.Reply(_("Restricted access option for %s is now \002on\002."), ci->name.c_str());
656 }
657 else if (params[1].equals_ci("OFF"))
658 {
659 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable restricted";
660 ci->Shrink<bool>("RESTRICTED");
661 source.Reply(_("Restricted access option for %s is now \002off\002."), ci->name.c_str());
662 }
663 else
664 this->OnSyntaxError(source, "RESTRICTED");
665 }
666
OnHelp(CommandSource & source,const Anope::string &)667 bool OnHelp(CommandSource &source, const Anope::string &) anope_override
668 {
669 this->SendSyntax(source);
670 source.Reply(" ");
671 source.Reply(_("Enables or disables the \002restricted access\002 option for a\n"
672 "channel. When \002restricted access\002 is set, users not on the access list will\n"
673 "instead be kicked and banned from the channel."));
674 return true;
675 }
676 };
677
678 class CommandCSSetSecure : public Command
679 {
680 public:
CommandCSSetSecure(Module * creator,const Anope::string & cname="chanserv/set/secure")681 CommandCSSetSecure(Module *creator, const Anope::string &cname = "chanserv/set/secure") : Command(creator, cname, 2, 2)
682 {
683 this->SetDesc(_("Activate security features"));
684 this->SetSyntax(_("\037channel\037 {ON | OFF}"));
685 }
686
Execute(CommandSource & source,const std::vector<Anope::string> & params)687 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
688 {
689 if (Anope::ReadOnly)
690 {
691 source.Reply(READ_ONLY_MODE);
692 return;
693 }
694
695 ChannelInfo *ci = ChannelInfo::Find(params[0]);
696 if (ci == NULL)
697 {
698 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
699 return;
700 }
701
702 EventReturn MOD_RESULT;
703 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
704 if (MOD_RESULT == EVENT_STOP)
705 return;
706
707 if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
708 {
709 source.Reply(ACCESS_DENIED);
710 return;
711 }
712
713 if (params[1].equals_ci("ON"))
714 {
715 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure";
716 ci->Extend<bool>("CS_SECURE");
717 source.Reply(_("Secure option for %s is now \002on\002."), ci->name.c_str());
718 }
719 else if (params[1].equals_ci("OFF"))
720 {
721 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure";
722 ci->Shrink<bool>("CS_SECURE");
723 source.Reply(_("Secure option for %s is now \002off\002."), ci->name.c_str());
724 }
725 else
726 this->OnSyntaxError(source, "SECURE");
727 }
728
OnHelp(CommandSource & source,const Anope::string &)729 bool OnHelp(CommandSource &source, const Anope::string &) anope_override
730 {
731 this->SendSyntax(source);
732 source.Reply(" ");
733 source.Reply(_("Enables or disables security features for a\n"
734 "channel. When \002SECURE\002 is set, only users who have\n"
735 "identified to services, and are not only recognized, will be\n"
736 "given access to channels from account-based access entries."));
737 return true;
738 }
739 };
740
741 class CommandCSSetSecureFounder : public Command
742 {
743 public:
CommandCSSetSecureFounder(Module * creator,const Anope::string & cname="chanserv/set/securefounder")744 CommandCSSetSecureFounder(Module *creator, const Anope::string &cname = "chanserv/set/securefounder") : Command(creator, cname, 2, 2)
745 {
746 this->SetDesc(_("Stricter control of channel founder status"));
747 this->SetSyntax(_("\037channel\037 {ON | OFF}"));
748 }
749
Execute(CommandSource & source,const std::vector<Anope::string> & params)750 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
751 {
752 if (Anope::ReadOnly)
753 {
754 source.Reply(READ_ONLY_MODE);
755 return;
756 }
757
758 ChannelInfo *ci = ChannelInfo::Find(params[0]);
759 if (ci == NULL)
760 {
761 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
762 return;
763 }
764
765 EventReturn MOD_RESULT;
766 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
767 if (MOD_RESULT == EVENT_STOP)
768 return;
769
770 if (MOD_RESULT != EVENT_ALLOW && (ci->HasExt("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && source.permission.empty() && !source.HasPriv("chanserv/administration"))
771 {
772 source.Reply(ACCESS_DENIED);
773 return;
774 }
775
776 if (params[1].equals_ci("ON"))
777 {
778 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure founder";
779 ci->Extend<bool>("SECUREFOUNDER");
780 source.Reply(_("Secure founder option for %s is now \002on\002."), ci->name.c_str());
781 }
782 else if (params[1].equals_ci("OFF"))
783 {
784 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure founder";
785 ci->Shrink<bool>("SECUREFOUNDER");
786 source.Reply(_("Secure founder option for %s is now \002off\002."), ci->name.c_str());
787 }
788 else
789 this->OnSyntaxError(source, "SECUREFOUNDER");
790 }
791
OnHelp(CommandSource & source,const Anope::string &)792 bool OnHelp(CommandSource &source, const Anope::string &) anope_override
793 {
794 this->SendSyntax(source);
795 source.Reply(" ");
796 source.Reply(_("Enables or disables the \002secure founder\002 option for a channel.\n"
797 "When \002secure founder\002 is set, only the real founder will be\n"
798 "able to drop the channel, change its founder and its successor,\n"
799 "and not those who have founder level access through\n"
800 "the access/qop command."));
801 return true;
802 }
803 };
804
805 class CommandCSSetSecureOps : public Command
806 {
807 public:
CommandCSSetSecureOps(Module * creator,const Anope::string & cname="chanserv/set/secureops")808 CommandCSSetSecureOps(Module *creator, const Anope::string &cname = "chanserv/set/secureops") : Command(creator, cname, 2, 2)
809 {
810 this->SetDesc(_("Stricter control of chanop status"));
811 this->SetSyntax(_("\037channel\037 {ON | OFF}"));
812 }
813
Execute(CommandSource & source,const std::vector<Anope::string> & params)814 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
815 {
816 if (Anope::ReadOnly)
817 {
818 source.Reply(READ_ONLY_MODE);
819 return;
820 }
821
822 ChannelInfo *ci = ChannelInfo::Find(params[0]);
823 if (ci == NULL)
824 {
825 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
826 return;
827 }
828
829 EventReturn MOD_RESULT;
830 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
831 if (MOD_RESULT == EVENT_STOP)
832 return;
833
834 if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
835 {
836 source.Reply(ACCESS_DENIED);
837 return;
838 }
839
840 if (params[1].equals_ci("ON"))
841 {
842 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable secure ops";
843 ci->Extend<bool>("SECUREOPS");
844 source.Reply(_("Secure ops option for %s is now \002on\002."), ci->name.c_str());
845 }
846 else if (params[1].equals_ci("OFF"))
847 {
848 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable secure ops";
849 ci->Shrink<bool>("SECUREOPS");
850 source.Reply(_("Secure ops option for %s is now \002off\002."), ci->name.c_str());
851 }
852 else
853 this->OnSyntaxError(source, "SECUREOPS");
854 }
855
OnHelp(CommandSource & source,const Anope::string &)856 bool OnHelp(CommandSource &source, const Anope::string &) anope_override
857 {
858 this->SendSyntax(source);
859 source.Reply(" ");
860 source.Reply(_("Enables or disables the \002secure ops\002 option for a channel.\n"
861 "When \002secure ops\002 is set, users who are not on the access list\n"
862 "will not be allowed channel operator status."));
863 return true;
864 }
865 };
866
867 class CommandCSSetSignKick : public Command
868 {
869 public:
CommandCSSetSignKick(Module * creator,const Anope::string & cname="chanserv/set/signkick")870 CommandCSSetSignKick(Module *creator, const Anope::string &cname = "chanserv/set/signkick") : Command(creator, cname, 2, 2)
871 {
872 this->SetDesc(_("Sign kicks that are done with the KICK command"));
873 this->SetSyntax(_("\037channel\037 {ON | LEVEL | OFF}"));
874 }
875
Execute(CommandSource & source,const std::vector<Anope::string> & params)876 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
877 {
878 if (Anope::ReadOnly)
879 {
880 source.Reply(READ_ONLY_MODE);
881 return;
882 }
883
884 ChannelInfo *ci = ChannelInfo::Find(params[0]);
885 if (ci == NULL)
886 {
887 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
888 return;
889 }
890
891 EventReturn MOD_RESULT;
892 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
893 if (MOD_RESULT == EVENT_STOP)
894 return;
895
896 if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
897 {
898 source.Reply(ACCESS_DENIED);
899 return;
900 }
901
902 if (params[1].equals_ci("ON"))
903 {
904 ci->Extend<bool>("SIGNKICK");
905 ci->Shrink<bool>("SIGNKICK_LEVEL");
906 source.Reply(_("Signed kick option for %s is now \002on\002."), ci->name.c_str());
907 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable sign kick";
908 }
909 else if (params[1].equals_ci("LEVEL"))
910 {
911 ci->Extend<bool>("SIGNKICK_LEVEL");
912 ci->Shrink<bool>("SIGNKICK");
913 source.Reply(_("Signed kick option for %s is now \002on\002, but depends of the\n"
914 "level of the user that is using the command."), ci->name.c_str());
915 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable sign kick level";
916 }
917 else if (params[1].equals_ci("OFF"))
918 {
919 ci->Shrink<bool>("SIGNKICK");
920 ci->Shrink<bool>("SIGNKICK_LEVEL");
921 source.Reply(_("Signed kick option for %s is now \002off\002."), ci->name.c_str());
922 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable sign kick";
923 }
924 else
925 this->OnSyntaxError(source, "SIGNKICK");
926 }
927
OnHelp(CommandSource & source,const Anope::string &)928 bool OnHelp(CommandSource &source, const Anope::string &) anope_override
929 {
930 this->SendSyntax(source);
931 source.Reply(" ");
932 source.Reply(_("Enables or disables signed kicks for a\n"
933 "channel. When \002SIGNKICK\002 is set, kicks issued with\n"
934 "the \002KICK\002 command will have the nick that used the\n"
935 "command in their reason.\n"
936 " \n"
937 "If you use \002LEVEL\002, those who have a level that is superior\n"
938 "or equal to the SIGNKICK level on the channel won't have their\n"
939 "kicks signed."));
940 return true;
941 }
942 };
943
944 class CommandCSSetSuccessor : public Command
945 {
946 public:
CommandCSSetSuccessor(Module * creator,const Anope::string & cname="chanserv/set/successor")947 CommandCSSetSuccessor(Module *creator, const Anope::string &cname = "chanserv/set/successor") : Command(creator, cname, 1, 2)
948 {
949 this->SetDesc(_("Set the successor for a channel"));
950 this->SetSyntax(_("\037channel\037 [\037nick\037]"));
951 }
952
Execute(CommandSource & source,const std::vector<Anope::string> & params)953 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
954 {
955 if (Anope::ReadOnly)
956 {
957 source.Reply(READ_ONLY_MODE);
958 return;
959 }
960
961 ChannelInfo *ci = ChannelInfo::Find(params[0]);
962 const Anope::string ¶m = params.size() > 1 ? params[1] : "";
963 if (ci == NULL)
964 {
965 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
966 return;
967 }
968
969 EventReturn MOD_RESULT;
970 FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, param));
971 if (MOD_RESULT == EVENT_STOP)
972 return;
973
974 if (MOD_RESULT != EVENT_ALLOW && (ci->HasExt("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && source.permission.empty() && !source.HasPriv("chanserv/administration"))
975 {
976 source.Reply(ACCESS_DENIED);
977 return;
978 }
979
980 NickCore *nc;
981
982 if (!param.empty())
983 {
984 const NickAlias *na = NickAlias::Find(param);
985
986 if (!na)
987 {
988 source.Reply(NICK_X_NOT_REGISTERED, param.c_str());
989 return;
990 }
991 if (na->nc == ci->GetFounder())
992 {
993 source.Reply(_("%s cannot be the successor on channel %s as they are the founder."), na->nick.c_str(), ci->name.c_str());
994 return;
995 }
996 nc = na->nc;
997 }
998 else
999 nc = NULL;
1000
1001 Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change the successor from " << (ci->GetSuccessor() ? ci->GetSuccessor()->display : "(none)") << " to " << (nc ? nc->display : "(none)");
1002
1003 ci->SetSuccessor(nc);
1004
1005 if (nc)
1006 source.Reply(_("Successor for \002%s\002 changed to \002%s\002."), ci->name.c_str(), nc->display.c_str());
1007 else
1008 source.Reply(_("Successor for \002%s\002 unset."), ci->name.c_str());
1009
1010 return;
1011 }
1012
OnHelp(CommandSource & source,const Anope::string &)1013 bool OnHelp(CommandSource &source, const Anope::string &) anope_override
1014 {
1015 this->SendSyntax(source);
1016 source.Reply(" ");
1017 source.Reply(_("Changes the successor of a channel. If the founder's\n"
1018 "nickname expires or is dropped while the channel is still\n"
1019 "registered, the successor will become the new founder of the\n"
1020 "channel. The successor's nickname must be a registered one.\n"
1021 "If there's no successor set, then the first nickname on the\n"
1022 "access list (with the highest access, if applicable) will\n"
1023 "become the new founder, but if the access list is empty, the\n"
1024 "channel will be dropped."));
1025 unsigned max_reg = Config->GetModule("chanserv")->Get<unsigned>("maxregistered");
1026 if (max_reg)
1027 {
1028 source.Reply(" ");
1029 source.Reply(_("Note, however, if the successor already has too many\n"
1030 "channels registered (%d), they will not be able to\n"
1031 "become the new founder and it will be as if the\n"
1032 "channel had no successor set."), max_reg);
1033 }
1034 return true;
1035 }
1036 };
1037
1038 class CommandCSSetNoexpire : public Command
1039 {
1040 public:
CommandCSSetNoexpire(Module * creator)1041 CommandCSSetNoexpire(Module *creator) : Command(creator, "chanserv/saset/noexpire", 2, 2)
1042 {
1043 this->SetDesc(_("Prevent the channel from expiring"));
1044 this->SetSyntax(_("\037channel\037 {ON | OFF}"));
1045 }
1046
Execute(CommandSource & source,const std::vector<Anope::string> & params)1047 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
1048 {
1049 if (Anope::ReadOnly)
1050 {
1051 source.Reply(READ_ONLY_MODE);
1052 return;
1053 }
1054
1055 ChannelInfo *ci = ChannelInfo::Find(params[0]);
1056 if (ci == NULL)
1057 {
1058 source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
1059 return;
1060 }
1061
1062 if (source.permission.empty() && !source.AccessFor(ci).HasPriv("SET"))
1063 {
1064 source.Reply(ACCESS_DENIED);
1065 return;
1066 }
1067
1068 if (params[1].equals_ci("ON"))
1069 {
1070 Log(LOG_ADMIN, source, this, ci) << "to enable noexpire";
1071 ci->Extend<bool>("CS_NO_EXPIRE");
1072 source.Reply(_("Channel %s \002will not\002 expire."), ci->name.c_str());
1073 }
1074 else if (params[1].equals_ci("OFF"))
1075 {
1076 Log(LOG_ADMIN, source, this, ci) << "to disable noexpire";
1077 ci->Shrink<bool>("CS_NO_EXPIRE");
1078 source.Reply(_("Channel %s \002will\002 expire."), ci->name.c_str());
1079 }
1080 else
1081 this->OnSyntaxError(source, "NOEXPIRE");
1082
1083 return;
1084 }
1085
OnHelp(CommandSource & source,const Anope::string &)1086 bool OnHelp(CommandSource &source, const Anope::string &) anope_override
1087 {
1088 this->SendSyntax(source);
1089 source.Reply(" ");
1090 source.Reply(_("Sets whether the given channel will expire. Setting this\n"
1091 "to ON prevents the channel from expiring."));
1092 return true;
1093 }
1094 };
1095
1096 class CSSet : public Module
1097 {
1098 SerializableExtensibleItem<bool> noautoop, peace, securefounder,
1099 restricted, secure, secureops, signkick, signkick_level, noexpire,
1100 persist;
1101
1102 struct KeepModes : SerializableExtensibleItem<bool>
1103 {
KeepModesCSSet::KeepModes1104 KeepModes(Module *m, const Anope::string &n) : SerializableExtensibleItem<bool>(m, n) { }
1105
ExtensibleSerializeCSSet::KeepModes1106 void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const anope_override
1107 {
1108 SerializableExtensibleItem<bool>::ExtensibleSerialize(e, s, data);
1109
1110 if (s->GetSerializableType()->GetName() != "ChannelInfo")
1111 return;
1112
1113 const ChannelInfo *ci = anope_dynamic_static_cast<const ChannelInfo *>(s);
1114 Anope::string modes;
1115 for (Channel::ModeList::const_iterator it = ci->last_modes.begin(); it != ci->last_modes.end(); ++it)
1116 {
1117 if (!modes.empty())
1118 modes += " ";
1119 modes += it->first;
1120 if (!it->second.empty())
1121 modes += "," + it->second;
1122 }
1123 data["last_modes"] << modes;
1124 }
1125
ExtensibleUnserializeCSSet::KeepModes1126 void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) anope_override
1127 {
1128 SerializableExtensibleItem<bool>::ExtensibleUnserialize(e, s, data);
1129
1130 if (s->GetSerializableType()->GetName() != "ChannelInfo")
1131 return;
1132
1133 ChannelInfo *ci = anope_dynamic_static_cast<ChannelInfo *>(s);
1134 Anope::string modes;
1135 data["last_modes"] >> modes;
1136 ci->last_modes.clear();
1137 for (spacesepstream sep(modes); sep.GetToken(modes);)
1138 {
1139 size_t c = modes.find(',');
1140 if (c == Anope::string::npos)
1141 ci->last_modes.insert(std::make_pair(modes, ""));
1142 else
1143 ci->last_modes.insert(std::make_pair(modes.substr(0, c), modes.substr(c + 1)));
1144 }
1145 }
1146 } keep_modes;
1147
1148 CommandCSSet commandcsset;
1149 CommandCSSetAutoOp commandcssetautoop;
1150 CommandCSSetBanType commandcssetbantype;
1151 CommandCSSetDescription commandcssetdescription;
1152 CommandCSSetFounder commandcssetfounder;
1153 CommandCSSetKeepModes commandcssetkeepmodes;
1154 CommandCSSetPeace commandcssetpeace;
1155 CommandCSSetPersist commandcssetpersist;
1156 CommandCSSetRestricted commandcssetrestricted;
1157 CommandCSSetSecure commandcssetsecure;
1158 CommandCSSetSecureFounder commandcssetsecurefounder;
1159 CommandCSSetSecureOps commandcssetsecureops;
1160 CommandCSSetSignKick commandcssetsignkick;
1161 CommandCSSetSuccessor commandcssetsuccessor;
1162 CommandCSSetNoexpire commandcssetnoexpire;
1163
1164 ExtensibleRef<bool> inhabit;
1165
1166 bool persist_lower_ts;
1167
1168 public:
CSSet(const Anope::string & modname,const Anope::string & creator)1169 CSSet(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
1170 noautoop(this, "NOAUTOOP"), peace(this, "PEACE"),
1171 securefounder(this, "SECUREFOUNDER"), restricted(this, "RESTRICTED"),
1172 secure(this, "CS_SECURE"), secureops(this, "SECUREOPS"), signkick(this, "SIGNKICK"),
1173 signkick_level(this, "SIGNKICK_LEVEL"), noexpire(this, "CS_NO_EXPIRE"),
1174 persist(this, "PERSIST"),
1175 keep_modes(this, "CS_KEEP_MODES"),
1176
1177 commandcsset(this), commandcssetautoop(this), commandcssetbantype(this),
1178 commandcssetdescription(this), commandcssetfounder(this), commandcssetkeepmodes(this),
1179 commandcssetpeace(this), commandcssetpersist(this), commandcssetrestricted(this),
1180 commandcssetsecure(this), commandcssetsecurefounder(this), commandcssetsecureops(this), commandcssetsignkick(this),
1181 commandcssetsuccessor(this), commandcssetnoexpire(this),
1182
1183 inhabit("inhabit")
1184 {
1185 }
1186
OnReload(Configuration::Conf * conf)1187 void OnReload(Configuration::Conf *conf) anope_override
1188 {
1189 persist_lower_ts = conf->GetModule(this)->Get<bool>("persist_lower_ts");
1190 }
1191
OnCreateChan(ChannelInfo * ci)1192 void OnCreateChan(ChannelInfo *ci) anope_override
1193 {
1194 ci->bantype = Config->GetModule(this)->Get<int>("defbantype", "2");
1195 }
1196
OnChannelSync(Channel * c)1197 void OnChannelSync(Channel *c) anope_override
1198 {
1199 if (c->ci && keep_modes.HasExt(c->ci))
1200 {
1201 Channel::ModeList ml = c->ci->last_modes;
1202 for (Channel::ModeList::iterator it = ml.begin(); it != ml.end(); ++it)
1203 c->SetMode(c->ci->WhoSends(), it->first, it->second);
1204 }
1205 }
1206
OnCheckKick(User * u,Channel * c,Anope::string & mask,Anope::string & reason)1207 EventReturn OnCheckKick(User *u, Channel *c, Anope::string &mask, Anope::string &reason) anope_override
1208 {
1209 if (!c->ci || !restricted.HasExt(c->ci) || c->MatchesList(u, "EXCEPT"))
1210 return EVENT_CONTINUE;
1211
1212 if (c->ci->AccessFor(u).empty() && (!c->ci->GetFounder() || u->Account() != c->ci->GetFounder()))
1213 return EVENT_STOP;
1214
1215 return EVENT_CONTINUE;
1216 }
1217
OnDelChan(ChannelInfo * ci)1218 void OnDelChan(ChannelInfo *ci) anope_override
1219 {
1220 if (ci->c && persist.HasExt(ci))
1221 ci->c->RemoveMode(ci->WhoSends(), "PERM", "", false);
1222 persist.Unset(ci);
1223 }
1224
OnChannelModeSet(Channel * c,MessageSource & setter,ChannelMode * mode,const Anope::string & param)1225 EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string ¶m) anope_override
1226 {
1227 if (c->ci)
1228 {
1229 /* Channel mode +P or so was set, mark this channel as persistent */
1230 if (mode->name == "PERM")
1231 persist.Set(c->ci, true);
1232
1233 if (mode->type != MODE_STATUS && !c->syncing && Me->IsSynced() && (!inhabit || !inhabit->HasExt(c)))
1234 c->ci->last_modes = c->GetModes();
1235 }
1236
1237 return EVENT_CONTINUE;
1238 }
1239
OnChannelModeUnset(Channel * c,MessageSource & setter,ChannelMode * mode,const Anope::string & param)1240 EventReturn OnChannelModeUnset(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string ¶m) anope_override
1241 {
1242 if (mode->name == "PERM")
1243 {
1244 if (c->ci)
1245 persist.Unset(c->ci);
1246 }
1247
1248 if (c->ci && mode->type != MODE_STATUS && !c->syncing && Me->IsSynced() && (!inhabit || !inhabit->HasExt(c)))
1249 c->ci->last_modes = c->GetModes();
1250
1251 return EVENT_CONTINUE;
1252 }
1253
OnJoinChannel(User * u,Channel * c)1254 void OnJoinChannel(User *u, Channel *c) anope_override
1255 {
1256 if (u->server != Me && persist_lower_ts && c->ci && persist.HasExt(c->ci) && c->creation_time > c->ci->time_registered)
1257 {
1258 Log(LOG_DEBUG) << "Changing TS of " << c->name << " from " << c->creation_time << " to " << c->ci->time_registered;
1259 c->creation_time = c->ci->time_registered;
1260 IRCD->SendChannel(c);
1261 c->Reset();
1262 }
1263 }
1264
OnSetCorrectModes(User * user,Channel * chan,AccessGroup & access,bool & give_modes,bool & take_modes)1265 void OnSetCorrectModes(User *user, Channel *chan, AccessGroup &access, bool &give_modes, bool &take_modes) anope_override
1266 {
1267 if (chan->ci)
1268 {
1269 if (noautoop.HasExt(chan->ci))
1270 give_modes = false;
1271 if (secureops.HasExt(chan->ci) && !user->HasPriv("chanserv/administration"))
1272 // This overrides what chanserv does because it is loaded after chanserv
1273 take_modes = true;
1274 }
1275 }
1276
OnPreChanExpire(ChannelInfo * ci,bool & expire)1277 void OnPreChanExpire(ChannelInfo *ci, bool &expire) anope_override
1278 {
1279 if (noexpire.HasExt(ci))
1280 expire = false;
1281 }
1282
OnChanInfo(CommandSource & source,ChannelInfo * ci,InfoFormatter & info,bool show_all)1283 void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override
1284 {
1285 if (!show_all)
1286 return;
1287
1288 if (peace.HasExt(ci))
1289 info.AddOption(_("Peace"));
1290 if (restricted.HasExt(ci))
1291 info.AddOption(_("Restricted access"));
1292 if (secure.HasExt(ci))
1293 info.AddOption(_("Security"));
1294 if (securefounder.HasExt(ci))
1295 info.AddOption(_("Secure founder"));
1296 if (secureops.HasExt(ci))
1297 info.AddOption(_("Secure ops"));
1298 if (signkick.HasExt(ci) || signkick_level.HasExt(ci))
1299 info.AddOption(_("Signed kicks"));
1300 if (persist.HasExt(ci))
1301 info.AddOption(_("Persistent"));
1302 if (noexpire.HasExt(ci))
1303 info.AddOption(_("No expire"));
1304 if (keep_modes.HasExt(ci))
1305 info.AddOption(_("Keep modes"));
1306 if (noautoop.HasExt(ci))
1307 info.AddOption(_("No auto-op"));
1308 }
1309 };
1310
1311 MODULE_INIT(CSSet)
1312