anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
ns_group.cpp (12710B)
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 #include "modules/ns_cert.h" 14 15 class NSGroupRequest : public IdentifyRequest 16 { 17 CommandSource source; 18 Command *cmd; 19 Anope::string nick; 20 Reference<NickAlias> target; 21 22 public: 23 NSGroupRequest(Module *o, CommandSource &src, Command *c, const Anope::string &n, NickAlias *targ, const Anope::string &pass) : IdentifyRequest(o, targ->nc->display, pass), source(src), cmd(c), nick(n), target(targ) { } 24 25 void OnSuccess() anope_override 26 { 27 User *u = source.GetUser(); 28 29 /* user changed nick? */ 30 if (u != NULL && u->nick != nick) 31 return; 32 33 if (!target || !target->nc) 34 return; 35 36 NickAlias *na = NickAlias::Find(nick); 37 /* If the nick is already registered, drop it. */ 38 if (na) 39 { 40 delete na; 41 } 42 43 na = new NickAlias(nick, target->nc); 44 na->time_registered = na->last_seen = Anope::CurTime; 45 46 if (u != NULL) 47 { 48 na->last_usermask = u->GetIdent() + "@" + u->GetDisplayedHost(); 49 na->last_realname = u->realname; 50 } 51 else 52 { 53 na->last_realname = source.GetNick(); 54 } 55 56 if (u != NULL) 57 { 58 IRCD->SendLogin(u, na); // protocol modules prevent this on unconfirmed accounts 59 u->Login(target->nc); 60 FOREACH_MOD(OnNickGroup, (u, target)); 61 } 62 63 Log(LOG_COMMAND, source, cmd) << "to make " << nick << " join group of " << target->nick << " (" << target->nc->display << ") (email: " << (!target->nc->email.empty() ? target->nc->email : "none") << ")"; 64 source.Reply(_("You are now in the group of \002%s\002."), target->nick.c_str()); 65 66 if (u) 67 u->lastnickreg = Anope::CurTime; 68 } 69 70 void OnFail() anope_override 71 { 72 User *u = source.GetUser(); 73 74 Log(LOG_COMMAND, source, cmd) << "and failed to group to " << target->nick; 75 if (NickAlias::Find(GetAccount()) != NULL) 76 { 77 source.Reply(PASSWORD_INCORRECT); 78 if (u) 79 u->BadPassword(); 80 } 81 else 82 source.Reply(NICK_X_NOT_REGISTERED, GetAccount().c_str()); 83 } 84 }; 85 86 class CommandNSGroup : public Command 87 { 88 public: 89 CommandNSGroup(Module *creator) : Command(creator, "nickserv/group", 0, 2) 90 { 91 this->SetDesc(_("Join a group")); 92 this->SetSyntax(_("\037[target]\037 \037[password]\037")); 93 this->AllowUnregistered(true); 94 } 95 96 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 97 { 98 User *user = source.GetUser(); 99 100 Anope::string nick; 101 if (params.empty()) 102 { 103 NickCore* core = source.GetAccount(); 104 if (core) 105 nick = core->display; 106 } 107 else 108 nick = params[0]; 109 110 if (nick.empty()) 111 { 112 this->SendSyntax(source); 113 return; 114 } 115 116 const Anope::string &pass = params.size() > 1 ? params[1] : ""; 117 118 if (Anope::ReadOnly) 119 { 120 source.Reply(_("Sorry, nickname grouping is temporarily disabled.")); 121 return; 122 } 123 124 if (!IRCD->IsNickValid(source.GetNick())) 125 { 126 source.Reply(NICK_CANNOT_BE_REGISTERED, source.GetNick().c_str()); 127 return; 128 } 129 130 if (Config->GetModule("nickserv")->Get<bool>("restrictopernicks")) 131 for (unsigned i = 0; i < Oper::opers.size(); ++i) 132 { 133 Oper *o = Oper::opers[i]; 134 135 if (user != NULL && !user->HasMode("OPER") && user->nick.find_ci(o->name) != Anope::string::npos) 136 { 137 source.Reply(NICK_CANNOT_BE_REGISTERED, user->nick.c_str()); 138 return; 139 } 140 } 141 142 NickAlias *target, *na = NickAlias::Find(source.GetNick()); 143 const Anope::string &guestnick = Config->GetModule("nickserv")->Get<const Anope::string>("guestnickprefix", "Guest"); 144 time_t reg_delay = Config->GetModule("nickserv")->Get<time_t>("regdelay"); 145 unsigned maxaliases = Config->GetModule(this->owner)->Get<unsigned>("maxaliases"); 146 if (!(target = NickAlias::Find(nick))) 147 source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); 148 else if (user && Anope::CurTime < user->lastnickreg + reg_delay) 149 source.Reply(_("Please wait %d seconds before using the GROUP command again."), (reg_delay + user->lastnickreg) - Anope::CurTime); 150 else if (target->nc->HasExt("NS_SUSPENDED")) 151 { 152 Log(LOG_COMMAND, source, this) << "and tried to group to SUSPENDED nick " << target->nick; 153 source.Reply(NICK_X_SUSPENDED, target->nick.c_str()); 154 } 155 else if (na && Config->GetModule(this->owner)->Get<bool>("nogroupchange")) 156 source.Reply(_("Your nick is already registered.")); 157 else if (na && *target->nc == *na->nc) 158 source.Reply(_("You are already a member of the group of \002%s\002."), target->nick.c_str()); 159 else if (na && na->nc != source.GetAccount()) 160 source.Reply(NICK_IDENTIFY_REQUIRED); 161 else if (maxaliases && target->nc->aliases->size() >= maxaliases && !target->nc->IsServicesOper()) 162 source.Reply(_("There are too many nicks in your group.")); 163 else if (source.GetNick().length() <= guestnick.length() + 7 && 164 source.GetNick().length() >= guestnick.length() + 1 && 165 !source.GetNick().find_ci(guestnick) && !source.GetNick().substr(guestnick.length()).find_first_not_of("1234567890")) 166 { 167 source.Reply(NICK_CANNOT_BE_REGISTERED, source.GetNick().c_str()); 168 } 169 else 170 { 171 bool ok = false; 172 if (!na && source.GetAccount() == target->nc) 173 ok = true; 174 175 NSCertList *cl = target->nc->GetExt<NSCertList>("certificates"); 176 if (user != NULL && !user->fingerprint.empty() && cl && cl->FindCert(user->fingerprint)) 177 ok = true; 178 179 if (ok == false && !pass.empty()) 180 { 181 NSGroupRequest *req = new NSGroupRequest(owner, source, this, source.GetNick(), target, pass); 182 FOREACH_MOD(OnCheckAuthentication, (source.GetUser(), req)); 183 req->Dispatch(); 184 } 185 else 186 { 187 NSGroupRequest req(owner, source, this, source.GetNick(), target, pass); 188 189 if (ok) 190 req.OnSuccess(); 191 else 192 req.OnFail(); 193 } 194 } 195 } 196 197 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override 198 { 199 this->SendSyntax(source); 200 source.Reply(" "); 201 source.Reply(_("This command makes your nickname join the \037target\037 nickname's\n" 202 "group. \037password\037 is the password of the target nickname.\n" 203 " \n" 204 "Joining a group will allow you to share your configuration,\n" 205 "memos, and channel privileges with all the nicknames in the\n" 206 "group, and much more!\n" 207 " \n" 208 "A group exists as long as it is useful. This means that even\n" 209 "if a nick of the group is dropped, you won't lose the\n" 210 "shared things described above, as long as there is at\n" 211 "least one nick remaining in the group.\n" 212 " \n" 213 "You may be able to use this command even if you have not registered\n" 214 "your nick yet. If your nick is already registered, you'll\n" 215 "need to identify yourself before using this command.\n" 216 " \n" 217 "It is recommended to use this command with a non-registered\n" 218 "nick because it will be registered automatically when\n" 219 "using this command. You may use it with a registered nick (to\n" 220 "change your group) only if your network administrators allowed\n" 221 "it.\n" 222 " \n" 223 "You can only be in one group at a time. Group merging is\n" 224 "not possible.\n" 225 " \n" 226 "\037Note\037: all the nicknames of a group have the same password.")); 227 return true; 228 } 229 }; 230 231 class CommandNSUngroup : public Command 232 { 233 public: 234 CommandNSUngroup(Module *creator) : Command(creator, "nickserv/ungroup", 0, 1) 235 { 236 this->SetDesc(_("Remove a nick from a group")); 237 this->SetSyntax(_("[\037nick\037]")); 238 } 239 240 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 241 { 242 Anope::string nick = !params.empty() ? params[0] : ""; 243 NickAlias *na = NickAlias::Find(!nick.empty() ? nick : source.GetNick()); 244 245 if (source.GetAccount()->aliases->size() == 1) 246 source.Reply(_("Your nick is not grouped to anything, you can't ungroup it.")); 247 else if (!na) 248 source.Reply(NICK_X_NOT_REGISTERED, !nick.empty() ? nick.c_str() : source.GetNick().c_str()); 249 else if (na->nc != source.GetAccount()) 250 source.Reply(_("Nick %s is not in your group."), na->nick.c_str()); 251 else 252 { 253 NickCore *oldcore = na->nc; 254 255 std::vector<NickAlias *>::iterator it = std::find(oldcore->aliases->begin(), oldcore->aliases->end(), na); 256 if (it != oldcore->aliases->end()) 257 oldcore->aliases->erase(it); 258 259 if (na->nick.equals_ci(oldcore->display)) 260 oldcore->SetDisplay(oldcore->aliases->front()); 261 262 NickCore *nc = new NickCore(na->nick); 263 na->nc = nc; 264 nc->aliases->push_back(na); 265 266 nc->pass = oldcore->pass; 267 if (!oldcore->email.empty()) 268 nc->email = oldcore->email; 269 nc->language = oldcore->language; 270 271 Log(LOG_COMMAND, source, this) << "to make " << na->nick << " leave group of " << oldcore->display << " (email: " << (!oldcore->email.empty() ? oldcore->email : "none") << ")"; 272 source.Reply(_("Nick %s has been ungrouped from %s."), na->nick.c_str(), oldcore->display.c_str()); 273 274 User *user = User::Find(na->nick, true); 275 if (user) 276 /* The user on the nick who was ungrouped may be identified to the old group, set -r */ 277 user->RemoveMode(source.service, "REGISTERED"); 278 } 279 } 280 281 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override 282 { 283 this->SendSyntax(source); 284 source.Reply(" "); 285 source.Reply(_("This command ungroups your nick, or if given, the specified nick,\n" 286 "from the group it is in. The ungrouped nick keeps its registration\n" 287 "time, password, email, greet, language, and url. Everything else\n" 288 "is reset. You may not ungroup yourself if there is only one nick in\n" 289 "your group.")); 290 return true; 291 } 292 }; 293 294 class CommandNSGList : public Command 295 { 296 public: 297 CommandNSGList(Module *creator) : Command(creator, "nickserv/glist", 0, 1) 298 { 299 this->SetDesc(_("Lists all nicknames in your group")); 300 } 301 302 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 303 { 304 const Anope::string &nick = !params.empty() ? params[0] : ""; 305 const NickCore *nc; 306 307 if (!nick.empty()) 308 { 309 const NickAlias *na = NickAlias::Find(nick); 310 if (!na) 311 { 312 source.Reply(NICK_X_NOT_REGISTERED, nick.c_str()); 313 return; 314 } 315 else if (na->nc != source.GetAccount() && !source.IsServicesOper()) 316 { 317 source.Reply(ACCESS_DENIED); 318 return; 319 } 320 321 nc = na->nc; 322 } 323 else 324 nc = source.GetAccount(); 325 326 ListFormatter list(source.GetAccount()); 327 list.AddColumn(_("Nick")).AddColumn(_("Expires")); 328 time_t nickserv_expire = Config->GetModule("nickserv")->Get<time_t>("expire", "21d"), 329 unconfirmed_expire = Config->GetModule("ns_register")->Get<time_t>("unconfirmedexpire", "1d"); 330 for (unsigned i = 0; i < nc->aliases->size(); ++i) 331 { 332 const NickAlias *na2 = nc->aliases->at(i); 333 334 Anope::string expires; 335 if (na2->HasExt("NS_NO_EXPIRE")) 336 expires = NO_EXPIRE; 337 else if (!nickserv_expire || Anope::NoExpire) 338 ; 339 else if (na2->nc->HasExt("UNCONFIRMED") && unconfirmed_expire) 340 expires = Anope::strftime(na2->time_registered + unconfirmed_expire, source.GetAccount()); 341 else 342 expires = Anope::strftime(na2->last_seen + nickserv_expire, source.GetAccount()); 343 344 ListFormatter::ListEntry entry; 345 entry["Nick"] = na2->nick; 346 entry["Expires"] = expires; 347 list.AddEntry(entry); 348 } 349 350 source.Reply(!nick.empty() ? _("List of nicknames in the group of \002%s\002:") : _("List of nicknames in your group:"), nc->display.c_str()); 351 std::vector<Anope::string> replies; 352 list.Process(replies); 353 354 for (unsigned i = 0; i < replies.size(); ++i) 355 source.Reply(replies[i]); 356 357 source.Reply(_("%d nickname(s) in the group."), nc->aliases->size()); 358 } 359 360 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override 361 { 362 if (source.IsServicesOper()) 363 source.Reply(_("Syntax: \002%s [\037nickname\037]\002\n" 364 " \n" 365 "Without a parameter, lists all nicknames that are in\n" 366 "your group.\n" 367 " \n" 368 "With a parameter, lists all nicknames that are in the\n" 369 "group of the given nick.\n" 370 "Specifying a nick is limited to \002Services Operators\002."), 371 source.command.c_str()); 372 else 373 source.Reply(_("Syntax: \002%s\002\n" 374 " \n" 375 "Lists all nicks in your group."), source.command.c_str()); 376 377 return true; 378 } 379 }; 380 381 class NSGroup : public Module 382 { 383 CommandNSGroup commandnsgroup; 384 CommandNSUngroup commandnsungroup; 385 CommandNSGList commandnsglist; 386 387 public: 388 NSGroup(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), 389 commandnsgroup(this), commandnsungroup(this), commandnsglist(this) 390 { 391 if (Config->GetModule("nickserv")->Get<bool>("nonicknameownership")) 392 throw ModuleException(modname + " can not be used with options:nonicknameownership enabled"); 393 } 394 }; 395 396 MODULE_INIT(NSGroup)