Anope IRC Services  Version 2.0
ns_ajoin.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 
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 
23 {
27 
28  AJoinEntry(Extensible *) : Serializable("AJoinEntry") { }
29 
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 
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 
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)
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 
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);
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> &params) 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 {
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 
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 */
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 
Serialize::Reference< NickCore > nc
Definition: account.h:47
Definition: bots.h:24
#define CHAN_X_INVALID
Definition: language.h:102
CoreExport bool ReadOnly
Definition: main.cpp:28
ExtensibleItem< AJoinList > ajoinlist
Definition: ns_ajoin.cpp:303
static NickAlias * Find(const Anope::string &nick)
Definition: nickalias.cpp:121
Anope::string key
Definition: ns_ajoin.cpp:26
Definition: hashcomp.h:84
virtual void SendInvite(const MessageSource &source, const Channel *c, User *u)
Definition: protocol.cpp:284
void push_back(char c)
Definition: anope.h:142
#define ACCESS_DENIED
Definition: language.h:73
static Serializable * Unserialize(Serializable *obj, Serialize::Data &sd)
Definition: ns_ajoin.cpp:51
void DoAdd(CommandSource &source, NickCore *nc, const Anope::string &chans, const Anope::string &keys)
Definition: ns_ajoin.cpp:121
Definition: users.h:34
#define NICK_X_NOT_REGISTERED
Definition: language.h:79
virtual void SendSVSJoin(const MessageSource &source, User *u, const Anope::string &chan, const Anope::string &param)
Definition: protocol.h:170
AJoinEntry(Extensible *)
Definition: ns_ajoin.cpp:28
#define READ_ONLY_MODE
Definition: language.h:71
void DoDel(CommandSource &source, NickCore *nc, const Anope::string &chans)
Definition: ns_ajoin.cpp:187
void SetDesc(const Anope::string &d)
Definition: command.cpp:130
bool equals_ci(const char *_str) const
Definition: anope.h:78
Serialize::Reference< ChannelInfo > ci
Definition: channels.h:46
T * Require(const Anope::string &name)
Definition: extensible.h:244
bool HasPriv(const Anope::string &priv) const
Definition: access.cpp:384
bool GetParam(const Anope::string &name, Anope::string &target) const
Definition: channels.cpp:513
static ChannelInfo * Find(const Anope::string &name)
Definition: regchannel.cpp:630
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: ns_ajoin.cpp:236
string substr(size_type pos=0, size_type n=npos) const
Definition: anope.h:277
~AJoinList()
Definition: ns_ajoin.cpp:83
Checker(const Anope::string &n)
Definition: serialize.h:202
std::map< Anope::string, Anope::string > ListEntry
Definition: lists.h:68
size_type length() const
Definition: anope.h:131
Definition: Config.cs:26
void Shrink(const Anope::string &name)
Definition: extensible.h:253
virtual void OnSyntaxError(CommandSource &source, const Anope::string &subcommand)
Definition: command.cpp:191
void Reply(const char *message,...)
Definition: command.cpp:96
Anope::string channel
Definition: ns_ajoin.cpp:25
CommandNSAJoin commandnsajoin
Definition: ns_ajoin.cpp:302
Anope::string display
Definition: account.h:113
Definition: ns_ajoin.cpp:22
#define anope_override
Definition: services.h:56
void Serialize(Serialize::Data &sd) const anope_override
Definition: ns_ajoin.cpp:41
bool empty() const
Definition: anope.h:126
CommandNSAJoin(Module *creator)
Definition: ns_ajoin.cpp:228
CoreExport IRCDProto * IRCD
Definition: protocol.cpp:23
#define MODULE_INIT(x)
Definition: modules.h:45
Serialize::Type ajoinentry_type
Definition: ns_ajoin.cpp:304
ChanUserList users
Definition: channels.h:56
ChanUserContainer * FindUser(User *u) const
Definition: channels.cpp:173
void SetSyntax(const Anope::string &s)
Definition: command.cpp:140
AccessGroup AccessFor(const User *u)
Definition: regchannel.cpp:413
size_t HasMode(const Anope::string &name, const Anope::string &param="")
Definition: channels.cpp:201
Anope::string stringify(const T &x)
Definition: anope.h:710
bool MatchesList(User *u, const Anope::string &list)
Definition: channels.cpp:713
static void ProcessModes()
Definition: modes.cpp:633
void DoList(CommandSource &source, NickCore *nc)
Definition: ns_ajoin.cpp:91
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: ns_ajoin.cpp:288
virtual bool IsChannelValid(const Anope::string &)
Definition: protocol.cpp:372
Serialize::Reference< NickCore > owner
Definition: ns_ajoin.cpp:24
static Channel * Find(const Anope::string &name)
Definition: channels.cpp:920
NSAJoin(const Anope::string &modname, const Anope::string &creator)
Definition: ns_ajoin.cpp:307
bool GetToken(Anope::string &token)
Definition: hashcomp.cpp:99
T anope_dynamic_static_cast(O ptr)
Definition: anope.h:774
void SendSyntax(CommandSource &)
Definition: command.cpp:145
NickCore * GetAccount()
Definition: command.cpp:36
const char * c_str() const
Definition: anope.h:117
void OnUserLogin(User *u) anope_override
Definition: ns_ajoin.cpp:317
Definition: logger.h:53
bool CanSVSJoin
Definition: protocol.h:51
T * GetExt(const Anope::string &name) const
Definition: extensible.h:213
static NickCore * Find(const Anope::string &nick)
Definition: nickcore.cpp:258
#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
Type(const Anope::string &n, unserialize_func f, Module *owner=NULL)
AJoinList(Extensible *)
Definition: ns_ajoin.cpp:18
#define NICK_X_SUSPENDED
Definition: language.h:82
Module * owner
Definition: service.h:84
~AJoinEntry()
Definition: ns_ajoin.cpp:30