anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
chanserv.cpp (14004B)
1 /* ChanServ core functions 2 * 3 * (C) 2003-2022 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 inline static Anope::string BotModes() 16 { 17 return Config->GetModule("botserv")->Get<Anope::string>("botmodes", 18 Config->GetModule("chanserv")->Get<Anope::string>("botmodes", "o") 19 ); 20 } 21 22 class ChanServCore : public Module, public ChanServService 23 { 24 Reference<BotInfo> ChanServ; 25 std::vector<Anope::string> defaults; 26 ExtensibleItem<bool> inhabit; 27 ExtensibleRef<bool> persist; 28 bool always_lower; 29 30 public: 31 ChanServCore(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, PSEUDOCLIENT | VENDOR), 32 ChanServService(this), inhabit(this, "inhabit"), persist("PERSIST"), always_lower(false) 33 { 34 } 35 36 void Hold(Channel *c) anope_override 37 { 38 /** A timer used to keep the BotServ bot/ChanServ in the channel 39 * after kicking the last user in a channel 40 */ 41 class ChanServTimer : public Timer 42 { 43 Reference<BotInfo> &ChanServ; 44 ExtensibleItem<bool> &inhabit; 45 Reference<Channel> c; 46 47 public: 48 /** Constructor 49 * @param chan The channel 50 */ 51 ChanServTimer(Reference<BotInfo> &cs, ExtensibleItem<bool> &i, Module *m, Channel *chan) : Timer(m, Config->GetModule(m)->Get<time_t>("inhabit", "15s")), ChanServ(cs), inhabit(i), c(chan) 52 { 53 if (!ChanServ || !c) 54 return; 55 inhabit.Set(c, true); 56 if (!c->ci || !c->ci->bi) 57 ChanServ->Join(c); 58 else if (!c->FindUser(c->ci->bi)) 59 c->ci->bi->Join(c); 60 61 /* Set +ntsi to prevent rejoin */ 62 c->SetMode(NULL, "NOEXTERNAL"); 63 c->SetMode(NULL, "TOPIC"); 64 c->SetMode(NULL, "SECRET"); 65 c->SetMode(NULL, "INVITE"); 66 } 67 68 /** Called when the delay is up 69 * @param The current time 70 */ 71 void Tick(time_t) anope_override 72 { 73 if (!c) 74 return; 75 76 /* In the event we don't part */ 77 c->RemoveMode(NULL, "SECRET"); 78 c->RemoveMode(NULL, "INVITE"); 79 80 inhabit.Unset(c); /* now we're done changing modes, unset inhabit */ 81 82 if (!c->ci || !c->ci->bi) 83 { 84 if (ChanServ) 85 ChanServ->Part(c); 86 } 87 /* If someone has rejoined this channel in the meantime, don't part the bot */ 88 else if (c->users.size() <= 1) 89 c->ci->bi->Part(c); 90 } 91 }; 92 93 if (inhabit.HasExt(c)) 94 return; 95 96 new ChanServTimer(ChanServ, inhabit, this->owner, c); 97 } 98 99 void OnReload(Configuration::Conf *conf) anope_override 100 { 101 const Anope::string &channick = conf->GetModule(this)->Get<const Anope::string>("client"); 102 103 if (channick.empty()) 104 throw ConfigException(Module::name + ": <client> must be defined"); 105 106 BotInfo *bi = BotInfo::Find(channick, true); 107 if (!bi) 108 throw ConfigException(Module::name + ": no bot named " + channick); 109 110 ChanServ = bi; 111 112 spacesepstream(conf->GetModule(this)->Get<const Anope::string>("defaults", "greet fantasy")).GetTokens(defaults); 113 if (defaults.empty()) 114 { 115 defaults.push_back("KEEPTOPIC"); 116 defaults.push_back("CS_SECURE"); 117 defaults.push_back("SECUREFOUNDER"); 118 defaults.push_back("SIGNKICK"); 119 } 120 else if (defaults[0].equals_ci("none")) 121 defaults.clear(); 122 123 always_lower = conf->GetModule(this)->Get<bool>("always_lower_ts"); 124 } 125 126 void OnBotDelete(BotInfo *bi) anope_override 127 { 128 if (bi == ChanServ) 129 ChanServ = NULL; 130 } 131 132 EventReturn OnBotPrivmsg(User *u, BotInfo *bi, Anope::string &message) anope_override 133 { 134 if (bi == ChanServ && Config->GetModule(this)->Get<bool>("opersonly") && !u->HasMode("OPER")) 135 { 136 u->SendMessage(bi, ACCESS_DENIED); 137 return EVENT_STOP; 138 } 139 140 return EVENT_CONTINUE; 141 } 142 143 void OnDelCore(NickCore *nc) anope_override 144 { 145 std::deque<ChannelInfo *> chans; 146 nc->GetChannelReferences(chans); 147 int max_reg = Config->GetModule(this)->Get<int>("maxregistered"); 148 149 for (unsigned i = 0; i < chans.size(); ++i) 150 { 151 ChannelInfo *ci = chans[i]; 152 153 if (ci->GetFounder() == nc) 154 { 155 NickCore *newowner = NULL; 156 if (ci->GetSuccessor() && ci->GetSuccessor() != nc && (ci->GetSuccessor()->IsServicesOper() || !max_reg || ci->GetSuccessor()->channelcount < max_reg)) 157 newowner = ci->GetSuccessor(); 158 else 159 { 160 const ChanAccess *highest = NULL; 161 for (unsigned j = 0; j < ci->GetAccessCount(); ++j) 162 { 163 const ChanAccess *ca = ci->GetAccess(j); 164 NickCore *anc = ca->GetAccount(); 165 166 if (!anc || (!anc->IsServicesOper() && max_reg && anc->channelcount >= max_reg) || (anc == nc)) 167 continue; 168 if (!highest || *ca > *highest) 169 highest = ca; 170 } 171 if (highest) 172 newowner = highest->GetAccount(); 173 } 174 175 if (newowner) 176 { 177 Log(LOG_NORMAL, "chanserv/drop", ChanServ) << "Transferring foundership of " << ci->name << " from deleted nick " << nc->display << " to " << newowner->display; 178 ci->SetFounder(newowner); 179 ci->SetSuccessor(NULL); 180 } 181 else 182 { 183 Log(LOG_NORMAL, "chanserv/drop", ChanServ) << "Deleting channel " << ci->name << " owned by deleted nick " << nc->display; 184 185 delete ci; 186 continue; 187 } 188 } 189 190 if (ci->GetSuccessor() == nc) 191 ci->SetSuccessor(NULL); 192 193 for (unsigned j = 0; j < ci->GetAccessCount(); ++j) 194 { 195 const ChanAccess *ca = ci->GetAccess(j); 196 197 if (ca->GetAccount() == nc) 198 { 199 delete ci->EraseAccess(j); 200 break; 201 } 202 } 203 204 for (unsigned j = 0; j < ci->GetAkickCount(); ++j) 205 { 206 const AutoKick *akick = ci->GetAkick(j); 207 if (akick->nc == nc) 208 { 209 ci->EraseAkick(j); 210 break; 211 } 212 } 213 } 214 } 215 216 void OnDelChan(ChannelInfo *ci) anope_override 217 { 218 /* remove access entries that are this channel */ 219 220 std::deque<Anope::string> chans; 221 ci->GetChannelReferences(chans); 222 223 for (unsigned i = 0; i < chans.size(); ++i) 224 { 225 ChannelInfo *c = ChannelInfo::Find(chans[i]); 226 if (!c) 227 continue; 228 229 for (unsigned j = 0; j < c->GetAccessCount(); ++j) 230 { 231 ChanAccess *a = c->GetAccess(j); 232 233 if (a->Mask().equals_ci(ci->name)) 234 { 235 delete a; 236 break; 237 } 238 } 239 } 240 241 if (ci->c) 242 { 243 ci->c->RemoveMode(ci->WhoSends(), "REGISTERED", "", false); 244 245 const Anope::string &require = Config->GetModule(this)->Get<const Anope::string>("require"); 246 if (!require.empty()) 247 ci->c->SetModes(ci->WhoSends(), false, "-%s", require.c_str()); 248 } 249 } 250 251 EventReturn OnPreHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 252 { 253 if (!params.empty() || source.c || source.service != *ChanServ) 254 return EVENT_CONTINUE; 255 source.Reply(_("\002%s\002 allows you to register and control various\n" 256 "aspects of channels. %s can often prevent\n" 257 "malicious users from \"taking over\" channels by limiting\n" 258 "who is allowed channel operator privileges. Available\n" 259 "commands are listed below; to use them, type\n" 260 "\002%s%s \037command\037\002. For more information on a\n" 261 "specific command, type \002%s%s HELP \037command\037\002.\n"), 262 ChanServ->nick.c_str(), ChanServ->nick.c_str(), Config->StrictPrivmsg.c_str(), ChanServ->nick.c_str(), Config->StrictPrivmsg.c_str(), ChanServ->nick.c_str(), ChanServ->nick.c_str(), source.command.c_str()); 263 return EVENT_CONTINUE; 264 } 265 266 void OnPostHelp(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 267 { 268 if (!params.empty() || source.c || source.service != *ChanServ) 269 return; 270 time_t chanserv_expire = Config->GetModule(this)->Get<time_t>("expire", "14d"); 271 if (chanserv_expire >= 86400) 272 source.Reply(_(" \n" 273 "Note that any channel which is not used for %d days\n" 274 "(i.e. which no user on the channel's access list enters\n" 275 "for that period of time) will be automatically dropped."), chanserv_expire / 86400); 276 if (source.IsServicesOper()) 277 source.Reply(_(" \n" 278 "Services Operators can also, depending on their access drop\n" 279 "any channel, view (and modify) the access, levels and akick\n" 280 "lists and settings for any channel.")); 281 } 282 283 void OnCheckModes(Reference<Channel> &c) anope_override 284 { 285 if (!c) 286 return; 287 288 if (c->ci) 289 c->SetMode(c->ci->WhoSends(), "REGISTERED", "", false); 290 else 291 c->RemoveMode(c->ci->WhoSends(), "REGISTERED", "", false); 292 293 const Anope::string &require = Config->GetModule(this)->Get<const Anope::string>("require"); 294 if (!require.empty()) 295 { 296 if (c->ci) 297 c->SetModes(c->ci->WhoSends(), false, "+%s", require.c_str()); 298 else 299 c->SetModes(c->ci->WhoSends(), false, "-%s", require.c_str()); 300 } 301 } 302 303 void OnCreateChan(ChannelInfo *ci) anope_override 304 { 305 /* Set default chan flags */ 306 for (unsigned i = 0; i < defaults.size(); ++i) 307 ci->Extend<bool>(defaults[i].upper()); 308 } 309 310 EventReturn OnCanSet(User *u, const ChannelMode *cm) anope_override 311 { 312 if (Config->GetModule(this)->Get<const Anope::string>("nomlock").find(cm->mchar) != Anope::string::npos 313 || Config->GetModule(this)->Get<const Anope::string>("require").find(cm->mchar) != Anope::string::npos) 314 return EVENT_STOP; 315 return EVENT_CONTINUE; 316 } 317 318 void OnChannelSync(Channel *c) anope_override 319 { 320 bool perm = c->HasMode("PERM") || (c->ci && persist && persist->HasExt(c->ci)); 321 if (!perm && !c->botchannel && (c->users.empty() || (c->users.size() == 1 && c->users.begin()->second->user->server == Me))) 322 { 323 this->Hold(c); 324 } 325 } 326 327 void OnLog(Log *l) anope_override 328 { 329 if (l->type == LOG_CHANNEL) 330 l->bi = ChanServ; 331 } 332 333 void OnExpireTick() anope_override 334 { 335 time_t chanserv_expire = Config->GetModule(this)->Get<time_t>("expire", "14d"); 336 337 if (!chanserv_expire || Anope::NoExpire || Anope::ReadOnly) 338 return; 339 340 for (registered_channel_map::const_iterator it = RegisteredChannelList->begin(), it_end = RegisteredChannelList->end(); it != it_end; ) 341 { 342 ChannelInfo *ci = it->second; 343 ++it; 344 345 bool expire = false; 346 347 if (Anope::CurTime - ci->last_used >= chanserv_expire) 348 { 349 if (ci->c) 350 { 351 time_t last_used = ci->last_used; 352 for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end && last_used == ci->last_used; ++cit) 353 ci->AccessFor(cit->second->user); 354 expire = last_used == ci->last_used; 355 } 356 else 357 expire = true; 358 } 359 360 FOREACH_MOD(OnPreChanExpire, (ci, expire)); 361 362 if (expire) 363 { 364 Log(LOG_NORMAL, "chanserv/expire", ChanServ) << "Expiring channel " << ci->name << " (founder: " << (ci->GetFounder() ? ci->GetFounder()->display : "(none)") << ")"; 365 FOREACH_MOD(OnChanExpire, (ci)); 366 delete ci; 367 } 368 } 369 } 370 371 EventReturn OnCheckDelete(Channel *c) anope_override 372 { 373 /* Do not delete this channel if ChanServ/a BotServ bot is inhabiting it */ 374 if (inhabit.HasExt(c)) 375 return EVENT_STOP; 376 377 return EVENT_CONTINUE; 378 } 379 380 void OnPostInit() anope_override 381 { 382 if (!persist) 383 return; 384 385 ChannelMode *perm = ModeManager::FindChannelModeByName("PERM"); 386 387 /* Find all persistent channels and create them, as we are about to finish burst to our uplink */ 388 for (registered_channel_map::iterator it = RegisteredChannelList->begin(), it_end = RegisteredChannelList->end(); it != it_end; ++it) 389 { 390 ChannelInfo *ci = it->second; 391 if (!persist->HasExt(ci)) 392 continue; 393 394 bool c; 395 ci->c = Channel::FindOrCreate(ci->name, c, ci->time_registered); 396 ci->c->syncing |= created; 397 398 if (perm) 399 { 400 ci->c->SetMode(NULL, perm); 401 } 402 else 403 { 404 if (!ci->bi) 405 ci->WhoSends()->Assign(NULL, ci); 406 if (ci->c->FindUser(ci->bi) == NULL) 407 { 408 ChannelStatus status(BotModes()); 409 ci->bi->Join(ci->c, &status); 410 } 411 } 412 } 413 414 } 415 416 void OnChanRegistered(ChannelInfo *ci) anope_override 417 { 418 if (!persist || !ci->c) 419 return; 420 /* Mark the channel as persistent */ 421 if (ci->c->HasMode("PERM")) 422 persist->Set(ci); 423 /* Persist may be in def cflags, set it here */ 424 else if (persist->HasExt(ci)) 425 ci->c->SetMode(NULL, "PERM"); 426 } 427 428 void OnJoinChannel(User *u, Channel *c) anope_override 429 { 430 if (always_lower && c->ci && c->creation_time > c->ci->time_registered) 431 { 432 Log(LOG_DEBUG) << "Changing TS of " << c->name << " from " << c->creation_time << " to " << c->ci->time_registered; 433 c->creation_time = c->ci->time_registered; 434 IRCD->SendChannel(c); 435 c->Reset(); 436 } 437 } 438 439 EventReturn OnChannelModeSet(Channel *c, MessageSource &setter, ChannelMode *mode, const Anope::string ¶m) anope_override 440 { 441 if (!always_lower && Anope::CurTime == c->creation_time && c->ci && setter.GetUser() && !setter.GetUser()->server->IsULined()) 442 { 443 ChanUserContainer *cu = c->FindUser(setter.GetUser()); 444 ChannelMode *cm = ModeManager::FindChannelModeByName("OP"); 445 if (cu && cm && !cu->status.HasMode(cm->mchar)) 446 { 447 /* Our -o and their mode change crossing, bounce their mode */ 448 c->RemoveMode(c->ci->WhoSends(), mode, param); 449 /* We don't set mlocks until after the join has finished processing, it will stack with this change, 450 * so there isn't much for the user to remove except -nt etc which is likely locked anyway. 451 */ 452 } 453 } 454 455 return EVENT_CONTINUE; 456 } 457 458 void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override 459 { 460 if (!show_all) 461 return; 462 463 time_t chanserv_expire = Config->GetModule(this)->Get<time_t>("expire", "14d"); 464 if (!ci->HasExt("CS_NO_EXPIRE") && chanserv_expire && !Anope::NoExpire && ci->last_used != Anope::CurTime) 465 info[_("Expires")] = Anope::strftime(ci->last_used + chanserv_expire, source.GetAccount()); 466 } 467 468 void OnSetCorrectModes(User *user, Channel *chan, AccessGroup &access, bool &give_modes, bool &take_modes) anope_override 469 { 470 if (always_lower) 471 // Since we always lower the TS, the other side will remove the modes if the channel ts lowers, so we don't 472 // have to worry about it 473 take_modes = false; 474 else if (ModeManager::FindChannelModeByName("REGISTERED")) 475 // Otherwise if the registered channel mode exists, we should remove modes if the channel is not +r 476 take_modes = !chan->HasMode("REGISTERED"); 477 } 478 }; 479 480 MODULE_INIT(ChanServCore)