anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
cs_flags.cpp (14714B)
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 14 static std::map<Anope::string, char> defaultFlags; 15 16 class FlagsChanAccess : public ChanAccess 17 { 18 public: 19 std::set<char> flags; 20 21 FlagsChanAccess(AccessProvider *p) : ChanAccess(p) 22 { 23 } 24 25 bool HasPriv(const Anope::string &priv) const anope_override 26 { 27 std::map<Anope::string, char>::iterator it = defaultFlags.find(priv); 28 if (it != defaultFlags.end() && this->flags.count(it->second) > 0) 29 return true; 30 return false; 31 } 32 33 Anope::string AccessSerialize() const anope_override 34 { 35 return Anope::string(this->flags.begin(), this->flags.end()); 36 } 37 38 void AccessUnserialize(const Anope::string &data) anope_override 39 { 40 for (unsigned i = data.length(); i > 0; --i) 41 this->flags.insert(data[i - 1]); 42 } 43 44 static Anope::string DetermineFlags(const ChanAccess *access) 45 { 46 if (access->provider->name == "access/flags") 47 return access->AccessSerialize(); 48 49 std::set<char> buffer; 50 51 for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it) 52 if (access->HasPriv(it->first)) 53 buffer.insert(it->second); 54 55 if (buffer.empty()) 56 return "(none)"; 57 else 58 return Anope::string(buffer.begin(), buffer.end()); 59 } 60 }; 61 62 class FlagsAccessProvider : public AccessProvider 63 { 64 public: 65 static FlagsAccessProvider *ap; 66 67 FlagsAccessProvider(Module *o) : AccessProvider(o, "access/flags") 68 { 69 ap = this; 70 } 71 72 ChanAccess *Create() anope_override 73 { 74 return new FlagsChanAccess(this); 75 } 76 }; 77 FlagsAccessProvider* FlagsAccessProvider::ap; 78 79 class CommandCSFlags : public Command 80 { 81 void DoModify(CommandSource &source, ChannelInfo *ci, Anope::string mask, const Anope::string &flags) 82 { 83 if (flags.empty()) 84 { 85 this->OnSyntaxError(source, ""); 86 return; 87 } 88 89 AccessGroup u_access = source.AccessFor(ci); 90 const ChanAccess *highest = u_access.Highest(); 91 const NickAlias *na = NULL; 92 93 if (IRCD->IsChannelValid(mask)) 94 { 95 if (Config->GetModule("chanserv")->Get<bool>("disallow_channel_access")) 96 { 97 source.Reply(_("Channels may not be on access lists.")); 98 return; 99 } 100 101 ChannelInfo *targ_ci = ChannelInfo::Find(mask); 102 if (targ_ci == NULL) 103 { 104 source.Reply(CHAN_X_NOT_REGISTERED, mask.c_str()); 105 return; 106 } 107 else if (ci == targ_ci) 108 { 109 source.Reply(_("You can't add a channel to its own access list.")); 110 return; 111 } 112 113 mask = targ_ci->name; 114 } 115 else 116 { 117 na = NickAlias::Find(mask); 118 if (!na && Config->GetModule("chanserv")->Get<bool>("disallow_hostmask_access")) 119 { 120 source.Reply(_("Masks and unregistered users may not be on access lists.")); 121 return; 122 } 123 else if (mask.find_first_of("!*@") == Anope::string::npos && !na) 124 { 125 User *targ = User::Find(mask, true); 126 if (targ != NULL) 127 mask = "*!*@" + targ->GetDisplayedHost(); 128 else 129 { 130 source.Reply(NICK_X_NOT_REGISTERED, mask.c_str()); 131 return; 132 } 133 } 134 135 if (na) 136 mask = na->nick; 137 } 138 139 ChanAccess *current = NULL; 140 unsigned current_idx; 141 std::set<char> current_flags; 142 bool override = false; 143 for (current_idx = ci->GetAccessCount(); current_idx > 0; --current_idx) 144 { 145 ChanAccess *access = ci->GetAccess(current_idx - 1); 146 if ((na && na->nc == access->GetAccount()) || mask.equals_ci(access->Mask())) 147 { 148 // Flags allows removing others that have the same access as you, 149 // but no other access system does. 150 if (highest && highest->provider != FlagsAccessProvider::ap && !u_access.founder) 151 // operator<= on the non-me entry! 152 if (*highest <= *access) 153 { 154 if (source.HasPriv("chanserv/access/modify")) 155 override = true; 156 else 157 { 158 source.Reply(ACCESS_DENIED); 159 return; 160 } 161 } 162 163 current = access; 164 Anope::string cur_flags = FlagsChanAccess::DetermineFlags(access); 165 for (unsigned j = cur_flags.length(); j > 0; --j) 166 current_flags.insert(cur_flags[j - 1]); 167 break; 168 } 169 } 170 171 unsigned access_max = Config->GetModule("chanserv")->Get<unsigned>("accessmax", "1024"); 172 if (access_max && ci->GetDeepAccessCount() >= access_max) 173 { 174 source.Reply(_("Sorry, you can only have %d access entries on a channel, including access entries from other channels."), access_max); 175 return; 176 } 177 178 Privilege *p = NULL; 179 bool add = true; 180 for (size_t i = 0; i < flags.length(); ++i) 181 { 182 char f = flags[i]; 183 switch (f) 184 { 185 case '+': 186 add = true; 187 break; 188 case '-': 189 add = false; 190 break; 191 case '*': 192 for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it) 193 { 194 bool has = current_flags.count(it->second); 195 // If we are adding a flag they already have or removing one they don't have, don't bother 196 if (add == has) 197 continue; 198 199 if (!u_access.HasPriv(it->first) && !u_access.founder) 200 { 201 if (source.HasPriv("chanserv/access/modify")) 202 override = true; 203 else 204 continue; 205 } 206 207 if (add) 208 current_flags.insert(it->second); 209 else 210 current_flags.erase(it->second); 211 } 212 break; 213 default: 214 p = PrivilegeManager::FindPrivilege(flags.substr(i)); 215 if (p != NULL && defaultFlags[p->name]) 216 { 217 f = defaultFlags[p->name]; 218 i = flags.length(); 219 } 220 221 for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it) 222 { 223 if (f != it->second) 224 continue; 225 else if (!u_access.HasPriv(it->first) && !u_access.founder) 226 { 227 if (source.HasPriv("chanserv/access/modify")) 228 override = true; 229 else 230 { 231 source.Reply(_("You cannot set the \002%c\002 flag."), f); 232 break; 233 } 234 } 235 if (add) 236 current_flags.insert(f); 237 else 238 current_flags.erase(f); 239 break; 240 } 241 } 242 } 243 if (current_flags.empty()) 244 { 245 if (current != NULL) 246 { 247 ci->EraseAccess(current_idx - 1); 248 FOREACH_MOD(OnAccessDel, (ci, source, current)); 249 delete current; 250 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to delete " << mask; 251 source.Reply(_("\002%s\002 removed from the %s access list."), mask.c_str(), ci->name.c_str()); 252 } 253 else 254 { 255 source.Reply(_("\002%s\002 not found on %s access list."), mask.c_str(), ci->name.c_str()); 256 } 257 return; 258 } 259 260 ServiceReference<AccessProvider> provider("AccessProvider", "access/flags"); 261 if (!provider) 262 return; 263 FlagsChanAccess *access = anope_dynamic_static_cast<FlagsChanAccess *>(provider->Create()); 264 access->SetMask(mask, ci); 265 access->creator = source.GetNick(); 266 access->last_seen = current ? current->last_seen : 0; 267 access->created = Anope::CurTime; 268 access->flags = current_flags; 269 270 if (current != NULL) 271 delete current; 272 273 ci->AddAccess(access); 274 275 FOREACH_MOD(OnAccessAdd, (ci, source, access)); 276 277 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to modify " << mask << "'s flags to " << access->AccessSerialize(); 278 if (p != NULL) 279 { 280 if (add) 281 source.Reply(_("Privilege \002%s\002 added to \002%s\002 on \002%s\002, new flags are +\002%s\002"), p->name.c_str(), access->Mask().c_str(), ci->name.c_str(), access->AccessSerialize().c_str()); 282 else 283 source.Reply(_("Privilege \002%s\002 removed from \002%s\002 on \002%s\002, new flags are +\002%s\002"), p->name.c_str(), access->Mask().c_str(), ci->name.c_str(), access->AccessSerialize().c_str()); 284 } 285 else 286 source.Reply(_("Flags for \002%s\002 on %s set to +\002%s\002"), access->Mask().c_str(), ci->name.c_str(), access->AccessSerialize().c_str()); 287 } 288 289 void DoList(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms) 290 { 291 const Anope::string &arg = params.size() > 2 ? params[2] : ""; 292 293 if (!ci->GetAccessCount()) 294 { 295 source.Reply(_("%s access list is empty."), ci->name.c_str()); 296 return; 297 } 298 299 ListFormatter list(source.GetAccount()); 300 301 list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Flags")).AddColumn(_("Creator")).AddColumn(_("Created")); 302 303 unsigned count = 0; 304 for (unsigned i = 0, end = ci->GetAccessCount(); i < end; ++i) 305 { 306 const ChanAccess *access = ci->GetAccess(i); 307 const Anope::string &flags = FlagsChanAccess::DetermineFlags(access); 308 309 if (!arg.empty()) 310 { 311 if (arg[0] == '+') 312 { 313 bool pass = true; 314 for (size_t j = 1; j < arg.length(); ++j) 315 if (flags.find(arg[j]) == Anope::string::npos) 316 pass = false; 317 if (pass == false) 318 continue; 319 } 320 else if (!Anope::Match(access->Mask(), arg)) 321 continue; 322 } 323 324 ListFormatter::ListEntry entry; 325 ++count; 326 entry["Number"] = stringify(i + 1); 327 entry["Mask"] = access->Mask(); 328 entry["Flags"] = flags; 329 entry["Creator"] = access->creator; 330 entry["Created"] = Anope::strftime(access->created, source.nc, true); 331 list.AddEntry(entry); 332 } 333 334 if (list.IsEmpty()) 335 source.Reply(_("No matching entries on %s access list."), ci->name.c_str()); 336 else 337 { 338 std::vector<Anope::string> replies; 339 list.Process(replies); 340 341 source.Reply(_("Flags list for %s"), ci->name.c_str()); 342 for (unsigned i = 0; i < replies.size(); ++i) 343 source.Reply(replies[i]); 344 if (count == ci->GetAccessCount()) 345 source.Reply(_("End of access list.")); 346 else 347 source.Reply(_("End of access list - %d/%d entries shown."), count, ci->GetAccessCount()); 348 } 349 } 350 351 void DoClear(CommandSource &source, ChannelInfo *ci) 352 { 353 if (!source.IsFounder(ci) && !source.HasPriv("chanserv/access/modify")) 354 source.Reply(ACCESS_DENIED); 355 else 356 { 357 ci->ClearAccess(); 358 359 FOREACH_MOD(OnAccessClear, (ci, source)); 360 361 source.Reply(_("Channel %s access list has been cleared."), ci->name.c_str()); 362 363 bool override = !source.IsFounder(ci); 364 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to clear the access list"; 365 } 366 367 return; 368 } 369 370 public: 371 CommandCSFlags(Module *creator) : Command(creator, "chanserv/flags", 1, 4) 372 { 373 this->SetDesc(_("Modify the list of privileged users")); 374 this->SetSyntax(_("\037channel\037 [MODIFY] \037mask\037 \037changes\037")); 375 this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | +\037flags\037]")); 376 this->SetSyntax(_("\037channel\037 CLEAR")); 377 } 378 379 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 380 { 381 const Anope::string &chan = params[0]; 382 const Anope::string &cmd = params.size() > 1 ? params[1] : ""; 383 384 ChannelInfo *ci = ChannelInfo::Find(chan); 385 if (ci == NULL) 386 { 387 source.Reply(CHAN_X_NOT_REGISTERED, chan.c_str()); 388 return; 389 } 390 391 bool is_list = cmd.empty() || cmd.equals_ci("LIST"); 392 bool has_access = false; 393 if (source.HasPriv("chanserv/access/modify")) 394 has_access = true; 395 else if (is_list && source.HasPriv("chanserv/access/list")) 396 has_access = true; 397 else if (is_list && source.AccessFor(ci).HasPriv("ACCESS_LIST")) 398 has_access = true; 399 else if (source.AccessFor(ci).HasPriv("ACCESS_CHANGE")) 400 has_access = true; 401 402 if (!has_access) 403 source.Reply(ACCESS_DENIED); 404 else if (Anope::ReadOnly && !is_list) 405 source.Reply(_("Sorry, channel access list modification is temporarily disabled.")); 406 else if (is_list) 407 this->DoList(source, ci, params); 408 else if (cmd.equals_ci("CLEAR")) 409 this->DoClear(source, ci); 410 else 411 { 412 Anope::string mask, flags; 413 if (cmd.equals_ci("MODIFY")) 414 { 415 mask = params.size() > 2 ? params[2] : ""; 416 flags = params.size() > 3 ? params[3] : ""; 417 } 418 else 419 { 420 mask = cmd; 421 flags = params.size() > 2 ? params[2] : ""; 422 } 423 424 this->DoModify(source, ci, mask, flags); 425 } 426 } 427 428 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override 429 { 430 this->SendSyntax(source); 431 source.Reply(" "); 432 source.Reply(_("%s is another way to modify the channel access list, similar to\n" 433 "the XOP and ACCESS methods."), source.command.c_str()); 434 source.Reply(" "); 435 source.Reply(_("The \002MODIFY\002 command allows you to modify the access list. If the mask is\n" 436 "not already on the access list it is added, then the changes are applied.\n" 437 "If the mask has no more flags, then the mask is removed from the access list.\n" 438 "Additionally, you may use +* or -* to add or remove all flags, respectively. You are\n" 439 "only able to modify the access list if you have the proper permission on the channel,\n" 440 "and even then you can only give other people access to the equivalent of what your access is.")); 441 source.Reply(" "); 442 source.Reply(_("The \002LIST\002 command allows you to list existing entries on the channel access list.\n" 443 "If a mask is given, the mask is wildcard matched against all existing entries on the\n" 444 "access list, and only those entries are returned. If a set of flags is given, only those\n" 445 "on the access list with the specified flags are returned.")); 446 source.Reply(" "); 447 source.Reply(_("The \002CLEAR\002 command clears the channel access list. This requires channel founder access.")); 448 source.Reply(" "); 449 source.Reply(_("The available flags are:")); 450 451 typedef std::multimap<char, Anope::string, ci::less> reverse_map; 452 reverse_map reverse; 453 for (std::map<Anope::string, char>::iterator it = defaultFlags.begin(), it_end = defaultFlags.end(); it != it_end; ++it) 454 reverse.insert(std::make_pair(it->second, it->first)); 455 456 for (reverse_map::iterator it = reverse.begin(), it_end = reverse.end(); it != it_end; ++it) 457 { 458 Privilege *p = PrivilegeManager::FindPrivilege(it->second); 459 if (p == NULL) 460 continue; 461 source.Reply(" %c - %s", it->first, Language::Translate(source.nc, p->desc.c_str())); 462 } 463 464 return true; 465 } 466 }; 467 468 class CSFlags : public Module 469 { 470 FlagsAccessProvider accessprovider; 471 CommandCSFlags commandcsflags; 472 473 public: 474 CSFlags(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), 475 accessprovider(this), commandcsflags(this) 476 { 477 this->SetPermanent(true); 478 479 } 480 481 void OnReload(Configuration::Conf *conf) anope_override 482 { 483 defaultFlags.clear(); 484 485 for (int i = 0; i < conf->CountBlock("privilege"); ++i) 486 { 487 Configuration::Block *priv = conf->GetBlock("privilege", i); 488 489 const Anope::string &pname = priv->Get<const Anope::string>("name"); 490 491 Privilege *p = PrivilegeManager::FindPrivilege(pname); 492 if (p == NULL) 493 continue; 494 495 const Anope::string &value = priv->Get<const Anope::string>("flag"); 496 if (value.empty()) 497 continue; 498 499 defaultFlags[p->name] = value[0]; 500 } 501 } 502 }; 503 504 MODULE_INIT(CSFlags)