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