anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
ns_ajoin.cpp (10542B)
1 /* NickServ 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 struct AJoinEntry; 15 16 struct AJoinList : Serialize::Checker<std::vector<AJoinEntry *> > 17 { 18 AJoinList(Extensible *) : Serialize::Checker<std::vector<AJoinEntry *> >("AJoinEntry") { } 19 ~AJoinList(); 20 }; 21 22 struct AJoinEntry : Serializable 23 { 24 Serialize::Reference<NickCore> owner; 25 Anope::string channel; 26 Anope::string key; 27 28 AJoinEntry(Extensible *) : Serializable("AJoinEntry") { } 29 30 ~AJoinEntry() 31 { 32 AJoinList *channels = owner->GetExt<AJoinList>("ajoinlist"); 33 if (channels) 34 { 35 std::vector<AJoinEntry *>::iterator it = std::find((*channels)->begin(), (*channels)->end(), this); 36 if (it != (*channels)->end()) 37 (*channels)->erase(it); 38 } 39 } 40 41 void Serialize(Serialize::Data &sd) const anope_override 42 { 43 if (!this->owner) 44 return; 45 46 sd["owner"] << this->owner->display; 47 sd["channel"] << this->channel; 48 sd["key"] << this->key; 49 } 50 51 static Serializable* Unserialize(Serializable *obj, Serialize::Data &sd) 52 { 53 Anope::string sowner; 54 55 sd["owner"] >> sowner; 56 57 NickCore *nc = NickCore::Find(sowner); 58 if (nc == NULL) 59 return NULL; 60 61 AJoinEntry *aj; 62 if (obj) 63 aj = anope_dynamic_static_cast<AJoinEntry *>(obj); 64 else 65 { 66 aj = new AJoinEntry(nc); 67 aj->owner = nc; 68 } 69 70 sd["channel"] >> aj->channel; 71 sd["key"] >> aj->key; 72 73 if (!obj) 74 { 75 AJoinList *channels = nc->Require<AJoinList>("ajoinlist"); 76 (*channels)->push_back(aj); 77 } 78 79 return aj; 80 } 81 }; 82 83 AJoinList::~AJoinList() 84 { 85 for (unsigned i = 0; i < (*this)->size(); ++i) 86 delete (*this)->at(i); 87 } 88 89 class CommandNSAJoin : public Command 90 { 91 void DoList(CommandSource &source, NickCore *nc) 92 { 93 AJoinList *channels = nc->Require<AJoinList>("ajoinlist"); 94 95 if ((*channels)->empty()) 96 source.Reply(_("%s's auto join list is empty."), nc->display.c_str()); 97 else 98 { 99 ListFormatter list(source.GetAccount()); 100 list.AddColumn(_("Number")).AddColumn(_("Channel")).AddColumn(_("Key")); 101 for (unsigned i = 0; i < (*channels)->size(); ++i) 102 { 103 AJoinEntry *aj = (*channels)->at(i); 104 ListFormatter::ListEntry entry; 105 entry["Number"] = stringify(i + 1); 106 entry["Channel"] = aj->channel; 107 entry["Key"] = aj->key; 108 list.AddEntry(entry); 109 } 110 111 source.Reply(_("%s's auto join list:"), nc->display.c_str()); 112 113 std::vector<Anope::string> replies; 114 list.Process(replies); 115 116 for (unsigned i = 0; i < replies.size(); ++i) 117 source.Reply(replies[i]); 118 } 119 } 120 121 void DoAdd(CommandSource &source, NickCore *nc, const Anope::string &chans, const Anope::string &keys) 122 { 123 AJoinList *channels = nc->Require<AJoinList>("ajoinlist"); 124 125 Anope::string addedchans; 126 Anope::string alreadyadded; 127 Anope::string invalidkey; 128 commasepstream ksep(keys, true); 129 commasepstream csep(chans); 130 for (Anope::string chan, key; csep.GetToken(chan);) 131 { 132 ksep.GetToken(key); 133 134 unsigned i = 0; 135 for (; i < (*channels)->size(); ++i) 136 if ((*channels)->at(i)->channel.equals_ci(chan)) 137 break; 138 139 if ((*channels)->size() >= Config->GetModule(this->owner)->Get<unsigned>("ajoinmax")) 140 { 141 source.Reply(_("Sorry, the maximum of %d auto join entries has been reached."), Config->GetModule(this->owner)->Get<unsigned>("ajoinmax")); 142 return; 143 } 144 else if (i != (*channels)->size()) 145 alreadyadded += chan + ", "; 146 else if (IRCD->IsChannelValid(chan) == false) 147 source.Reply(CHAN_X_INVALID, chan.c_str()); 148 else 149 { 150 Channel *c = Channel::Find(chan); 151 Anope::string k; 152 if (c && c->GetParam("KEY", k) && key != k) 153 { 154 invalidkey += chan + ", "; 155 continue; 156 } 157 158 AJoinEntry *entry = new AJoinEntry(nc); 159 entry->owner = nc; 160 entry->channel = chan; 161 entry->key = key; 162 (*channels)->push_back(entry); 163 addedchans += chan + ", "; 164 } 165 } 166 167 if (!alreadyadded.empty()) 168 { 169 alreadyadded = alreadyadded.substr(0, alreadyadded.length() - 2); 170 source.Reply(_("%s is already on %s's auto join list."), alreadyadded.c_str(), nc->display.c_str()); 171 } 172 173 if (!invalidkey.empty()) 174 { 175 invalidkey = invalidkey.substr(0, invalidkey.length() - 2); 176 source.Reply(_("%s had an invalid key specified, and was thus ignored."), invalidkey.c_str()); 177 } 178 179 if (addedchans.empty()) 180 return; 181 182 addedchans = addedchans.substr(0, addedchans.length() - 2); 183 Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to ADD channel " << addedchans << " to " << nc->display; 184 source.Reply(_("%s added to %s's auto join list."), addedchans.c_str(), nc->display.c_str()); 185 } 186 187 void DoDel(CommandSource &source, NickCore *nc, const Anope::string &chans) 188 { 189 AJoinList *channels = nc->Require<AJoinList>("ajoinlist"); 190 Anope::string delchans; 191 Anope::string notfoundchans; 192 commasepstream sep(chans); 193 194 for (Anope::string chan; sep.GetToken(chan);) 195 { 196 unsigned i = 0; 197 for (; i < (*channels)->size(); ++i) 198 if ((*channels)->at(i)->channel.equals_ci(chan)) 199 break; 200 201 if (i == (*channels)->size()) 202 notfoundchans += chan + ", "; 203 else 204 { 205 delete (*channels)->at(i); 206 delchans += chan + ", "; 207 } 208 } 209 210 if (!notfoundchans.empty()) 211 { 212 notfoundchans = notfoundchans.substr(0, notfoundchans.length() - 2); 213 source.Reply(_("%s was not found on %s's auto join list."), notfoundchans.c_str(), nc->display.c_str()); 214 } 215 216 if (delchans.empty()) 217 return; 218 219 delchans = delchans.substr(0, delchans.length() - 2); 220 Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to DELETE channel " << delchans << " from " << nc->display; 221 source.Reply(_("%s was removed from %s's auto join list."), delchans.c_str(), nc->display.c_str()); 222 223 if ((*channels)->empty()) 224 nc->Shrink<AJoinList>("ajoinlist"); 225 } 226 227 public: 228 CommandNSAJoin(Module *creator) : Command(creator, "nickserv/ajoin", 1, 4) 229 { 230 this->SetDesc(_("Manage your auto join list")); 231 this->SetSyntax(_("ADD [\037nickname\037] \037channel\037 [\037key\037]")); 232 this->SetSyntax(_("DEL [\037nickname\037] \037channel\037")); 233 this->SetSyntax(_("LIST [\037nickname\037]")); 234 } 235 236 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 237 { 238 const Anope::string &cmd = params[0]; 239 Anope::string nick, param, param2; 240 241 if (cmd.equals_ci("LIST")) 242 nick = params.size() > 1 ? params[1] : ""; 243 else 244 nick = (params.size() > 2 && IRCD->IsChannelValid(params[2])) ? params[1] : ""; 245 246 NickCore *nc; 247 if (!nick.empty()) 248 { 249 const NickAlias *na = NickAlias::Find(nick); 250 if (na == NULL) 251 { 252 source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); 253 return; 254 } 255 else if (na->nc != source.GetAccount() && !source.HasCommand("nickserv/ajoin")) 256 { 257 source.Reply(ACCESS_DENIED); 258 return; 259 } 260 261 nc = na->nc; 262 param = params.size() > 2 ? params[2] : ""; 263 param2 = params.size() > 3 ? params[3] : ""; 264 } 265 else 266 { 267 nc = source.nc; 268 param = params.size() > 1 ? params[1] : ""; 269 param2 = params.size() > 2 ? params[2] : ""; 270 } 271 272 if (cmd.equals_ci("LIST")) 273 return this->DoList(source, nc); 274 else if (nc->HasExt("NS_SUSPENDED")) 275 source.Reply(NICK_X_SUSPENDED, nc->display.c_str()); 276 else if (param.empty()) 277 this->OnSyntaxError(source, ""); 278 else if (Anope::ReadOnly) 279 source.Reply(READ_ONLY_MODE); 280 else if (cmd.equals_ci("ADD")) 281 return this->DoAdd(source, nc, param, param2); 282 else if (cmd.equals_ci("DEL")) 283 return this->DoDel(source, nc, param); 284 else 285 this->OnSyntaxError(source, ""); 286 } 287 288 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override 289 { 290 this->SendSyntax(source); 291 source.Reply(" "); 292 source.Reply(_("This command manages your auto join list. When you identify\n" 293 "you will automatically join the channels on your auto join list.\n" 294 "Services Operators may provide a nick to modify other users'\n" 295 "auto join lists.")); 296 return true; 297 } 298 }; 299 300 class NSAJoin : public Module 301 { 302 CommandNSAJoin commandnsajoin; 303 ExtensibleItem<AJoinList> ajoinlist; 304 Serialize::Type ajoinentry_type; 305 306 public: 307 NSAJoin(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), 308 commandnsajoin(this), ajoinlist(this, "ajoinlist"), 309 ajoinentry_type("AJoinEntry", AJoinEntry::Unserialize) 310 { 311 312 if (!IRCD || !IRCD->CanSVSJoin) 313 throw ModuleException("Your IRCd does not support SVSJOIN"); 314 315 } 316 317 void OnUserLogin(User *u) anope_override 318 { 319 BotInfo *NickServ = Config->GetClient("NickServ"); 320 if (!NickServ) 321 return; 322 323 AJoinList *channels = u->Account()->GetExt<AJoinList>("ajoinlist"); 324 if (channels == NULL) 325 return; 326 327 /* Set +r now, so we can ajoin users into +R channels */ 328 ModeManager::ProcessModes(); 329 330 for (unsigned i = 0; i < (*channels)->size(); ++i) 331 { 332 AJoinEntry *entry = (*channels)->at(i); 333 Channel *c = Channel::Find(entry->channel); 334 ChannelInfo *ci; 335 336 if (c) 337 ci = c->ci; 338 else 339 ci = ChannelInfo::Find(entry->channel); 340 341 bool need_invite = false; 342 Anope::string key = entry->key; 343 AccessGroup u_access; 344 345 if (ci != NULL) 346 { 347 if (ci->HasExt("CS_SUSPENDED")) 348 continue; 349 u_access = ci->AccessFor(u); 350 } 351 if (c != NULL) 352 { 353 if (c->FindUser(u) != NULL) 354 continue; 355 else if (c->HasMode("OPERONLY") && !u->HasMode("OPER")) 356 continue; 357 else if (c->HasMode("ADMINONLY") && !u->HasMode("ADMIN")) 358 continue; 359 else if (c->HasMode("SSL") && !(u->HasMode("SSL") || u->HasExt("ssl"))) 360 continue; 361 else if (c->MatchesList(u, "BAN") == true && c->MatchesList(u, "EXCEPT") == false) 362 need_invite = true; 363 else if (c->HasMode("INVITE") && c->MatchesList(u, "INVITEOVERRIDE") == false) 364 need_invite = true; 365 366 if (c->HasMode("KEY")) 367 { 368 Anope::string k; 369 if (c->GetParam("KEY", k)) 370 { 371 if (u_access.HasPriv("GETKEY")) 372 key = k; 373 else if (key != k) 374 need_invite = true; 375 } 376 } 377 if (c->HasMode("LIMIT")) 378 { 379 Anope::string l; 380 if (c->GetParam("LIMIT", l)) 381 { 382 try 383 { 384 unsigned limit = convertTo<unsigned>(l); 385 if (c->users.size() >= limit) 386 need_invite = true; 387 } 388 catch (const ConvertException &) { } 389 } 390 } 391 } 392 393 if (need_invite && c != NULL) 394 { 395 if (!u_access.HasPriv("INVITE")) 396 continue; 397 IRCD->SendInvite(NickServ, c, u); 398 } 399 400 IRCD->SendSVSJoin(NickServ, u, entry->channel, key); 401 } 402 } 403 }; 404 405 MODULE_INIT(NSAJoin)