Anope IRC Services  Version 1.8
cs_xop.c
Go to the documentation of this file.
1 /* ChanServ 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  */
13 /*************************************************************************/
14 
15 #include "module.h"
16 
17 static int do_xop(User * u, char *xname, int xlev, int *xmsgs);
18 static int do_aop(User * u);
19 static int do_hop(User * u);
20 static int do_sop(User * u);
21 static int do_vop(User * u);
22 
23 static void myChanServHelp(User * u);
24 
25 static int xop_msgs[4][14] = {
26  {CHAN_AOP_SYNTAX,
27  CHAN_AOP_DISABLED,
28  CHAN_AOP_NICKS_ONLY,
29  CHAN_AOP_ADDED,
30  CHAN_AOP_MOVED,
31  CHAN_AOP_NO_SUCH_ENTRY,
32  CHAN_AOP_NOT_FOUND,
33  CHAN_AOP_NO_MATCH,
34  CHAN_AOP_DELETED,
35  CHAN_AOP_DELETED_ONE,
36  CHAN_AOP_DELETED_SEVERAL,
37  CHAN_AOP_LIST_EMPTY,
38  CHAN_AOP_LIST_HEADER,
39  CHAN_AOP_CLEAR},
40  {CHAN_SOP_SYNTAX,
41  CHAN_SOP_DISABLED,
42  CHAN_SOP_NICKS_ONLY,
43  CHAN_SOP_ADDED,
44  CHAN_SOP_MOVED,
45  CHAN_SOP_NO_SUCH_ENTRY,
46  CHAN_SOP_NOT_FOUND,
47  CHAN_SOP_NO_MATCH,
48  CHAN_SOP_DELETED,
49  CHAN_SOP_DELETED_ONE,
50  CHAN_SOP_DELETED_SEVERAL,
51  CHAN_SOP_LIST_EMPTY,
52  CHAN_SOP_LIST_HEADER,
53  CHAN_SOP_CLEAR},
54  {CHAN_VOP_SYNTAX,
55  CHAN_VOP_DISABLED,
56  CHAN_VOP_NICKS_ONLY,
57  CHAN_VOP_ADDED,
58  CHAN_VOP_MOVED,
59  CHAN_VOP_NO_SUCH_ENTRY,
60  CHAN_VOP_NOT_FOUND,
61  CHAN_VOP_NO_MATCH,
62  CHAN_VOP_DELETED,
63  CHAN_VOP_DELETED_ONE,
64  CHAN_VOP_DELETED_SEVERAL,
65  CHAN_VOP_LIST_EMPTY,
66  CHAN_VOP_LIST_HEADER,
67  CHAN_VOP_CLEAR},
68  {CHAN_HOP_SYNTAX,
69  CHAN_HOP_DISABLED,
70  CHAN_HOP_NICKS_ONLY,
71  CHAN_HOP_ADDED,
72  CHAN_HOP_MOVED,
73  CHAN_HOP_NO_SUCH_ENTRY,
74  CHAN_HOP_NOT_FOUND,
75  CHAN_HOP_NO_MATCH,
76  CHAN_HOP_DELETED,
77  CHAN_HOP_DELETED_ONE,
78  CHAN_HOP_DELETED_SEVERAL,
79  CHAN_HOP_LIST_EMPTY,
80  CHAN_HOP_LIST_HEADER,
81  CHAN_HOP_CLEAR}
82 };
83 
90 int AnopeInit(int argc, char **argv)
91 {
92  Command *c;
93 
94  moduleAddAuthor("Anope");
95  moduleAddVersion(VERSION_STRING);
97 
98  c = createCommand("AOP", do_aop, NULL, CHAN_HELP_AOP, -1, -1, -1, -1);
100  c = createCommand("HOP", do_hop, NULL, CHAN_HELP_HOP, -1, -1, -1, -1);
102  c = createCommand("SOP", do_sop, NULL, CHAN_HELP_SOP, -1, -1, -1, -1);
104  c = createCommand("VOP", do_vop, NULL, CHAN_HELP_VOP, -1, -1, -1, -1);
106 
108 
109  return MOD_CONT;
110 }
111 
115 void AnopeFini(void)
116 {
117 
118 }
119 
120 
121 
126 static void myChanServHelp(User * u)
127 {
128  notice_lang(s_ChanServ, u, CHAN_HELP_CMD_SOP);
129  notice_lang(s_ChanServ, u, CHAN_HELP_CMD_AOP);
130  if (ircd->halfop) {
131  notice_lang(s_ChanServ, u, CHAN_HELP_CMD_HOP);
132  }
133  notice_lang(s_ChanServ, u, CHAN_HELP_CMD_VOP);
134 }
135 
141 static int do_aop(User * u)
142 {
143  return do_xop(u, "AOP", ACCESS_AOP, xop_msgs[0]);
144 }
145 
146 /*************************************************************************/
147 
148 static int do_hop(User * u)
149 {
150  if (ircd->halfop)
151  return do_xop(u, "HOP", ACCESS_HOP, xop_msgs[3]);
152  return MOD_CONT;
153 }
154 
155 /*************************************************************************/
156 
157 static int do_sop(User * u)
158 {
159  return do_xop(u, "SOP", ACCESS_SOP, xop_msgs[1]);
160 }
161 
162 /*************************************************************************/
163 
164 static int do_vop(User * u)
165 {
166  return do_xop(u, "VOP", ACCESS_VOP, xop_msgs[2]);
167 }
168 
169 /* `last' is set to the last index this routine was called with
170  * `perm' is incremented whenever a permission-denied error occurs
171  */
172 
173 static int xop_del(User * u, ChannelInfo * ci, ChanAccess * access, int *perm, int uacc, int xlev)
174 {
175  char *nick;
176  if (!access->in_use || !access->nc || access->level != xlev)
177  return 0;
178  nick = access->nc->display;
179  if (!is_services_admin(u) && uacc <= access->level) {
180  (*perm)++;
181  return 0;
182  }
183  access->nc = NULL;
184  access->in_use = 0;
185  send_event(EVENT_ACCESS_DEL, 3, ci->name, u->nick, nick);
186  return 1;
187 }
188 
189 static int xop_del_callback(User * u, int num, va_list args)
190 {
191  ChannelInfo *ci = va_arg(args, ChannelInfo *);
192  int *last = va_arg(args, int *);
193  int *perm = va_arg(args, int *);
194  int uacc = va_arg(args, int);
195  int xlev = va_arg(args, int);
196 
197  if (num < 1 || num > ci->accesscount)
198  return 0;
199  *last = num;
200 
201  return xop_del(u, ci, &ci->access[num - 1], perm, uacc, xlev);
202 }
203 
204 
205 static int xop_list(User * u, int index, ChannelInfo * ci,
206  int *sent_header, int xlev, int xmsg)
207 {
208  ChanAccess *access = &ci->access[index];
209 
210  if (!access->in_use || access->level != xlev)
211  return 0;
212 
213  if (!*sent_header) {
214  notice_lang(s_ChanServ, u, xmsg, ci->name);
215  *sent_header = 1;
216  }
217 
218  notice_lang(s_ChanServ, u, CHAN_XOP_LIST_FORMAT, index + 1,
219  access->nc->display);
220  return 1;
221 }
222 
223 static int xop_list_callback(User * u, int num, va_list args)
224 {
225  ChannelInfo *ci = va_arg(args, ChannelInfo *);
226  int *sent_header = va_arg(args, int *);
227  int xlev = va_arg(args, int);
228  int xmsg = va_arg(args, int);
229 
230  if (num < 1 || num > ci->accesscount)
231  return 0;
232 
233  return xop_list(u, num - 1, ci, sent_header, xlev, xmsg);
234 }
235 
236 
237 static int do_xop(User * u, char *xname, int xlev, int *xmsgs)
238 {
239  char *chan = strtok(NULL, " ");
240  char *cmd = strtok(NULL, " ");
241  char *nick = strtok(NULL, " ");
242  char event_access[BUFSIZE];
243 
244  ChannelInfo *ci;
245  NickAlias *na;
246  NickCore *nc;
247 
248  int i;
249  int change = 0;
250  short ulev;
251  int is_list = (cmd && stricmp(cmd, "LIST") == 0);
252  int is_servadmin = is_services_admin(u);
253  ChanAccess *access;
254 
255  /* If CLEAR, we don't need any parameters.
256  * If LIST, we don't *require* any parameters, but we can take any.
257  * If DEL or ADD we require a nick. */
258  if (!cmd || ((is_list || !stricmp(cmd, "CLEAR")) ? 0 : !nick)) {
259  syntax_error(s_ChanServ, u, xname, xmsgs[0]);
260  } else if (!(ci = cs_findchan(chan))) {
261  notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
262  } else if (ci->flags & CI_VERBOTEN) {
263  notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
264  } else if (!(ci->flags & CI_XOP)) {
265  notice_lang(s_ChanServ, u, CHAN_XOP_ACCESS, s_ChanServ);
266  } else if (stricmp(cmd, "ADD") == 0) {
267  if (readonly) {
268  notice_lang(s_ChanServ, u, xmsgs[1]);
269  return MOD_CONT;
270  }
271 
272  ulev = get_access(u, ci);
273 
274  if ((xlev >= ulev || ulev < ACCESS_AOP) && !is_servadmin) {
275  notice_lang(s_ChanServ, u, PERMISSION_DENIED);
276  return MOD_CONT;
277  }
278 
279  na = findnick(nick);
280  if (!na) {
281  notice_lang(s_ChanServ, u, xmsgs[2]);
282  return MOD_CONT;
283  } else if (na->status & NS_VERBOTEN) {
284  notice_lang(s_ChanServ, u, NICK_X_FORBIDDEN, na->nick);
285  return MOD_CONT;
286  }
287 
288  nc = na->nc;
289  for (access = ci->access, i = 0; i < ci->accesscount;
290  access++, i++) {
291  if (access->nc == nc) {
295  if ((access->level >= ulev) && (!is_servadmin)) {
296  notice_lang(s_ChanServ, u, PERMISSION_DENIED);
297  return MOD_CONT;
298  }
299  change++;
300  break;
301  }
302  }
303 
304  if (!change) {
305  if (i < CSAccessMax) {
306  ci->accesscount++;
307  ci->access =
308  srealloc(ci->access,
309  sizeof(ChanAccess) * ci->accesscount);
310  } else {
311  notice_lang(s_ChanServ, u, CHAN_XOP_REACHED_LIMIT,
312  CSAccessMax);
313  return MOD_CONT;
314  }
315 
316  access = &ci->access[i];
317  access->nc = nc;
318  }
319 
320  access->in_use = 1;
321  access->level = xlev;
322  access->last_seen = 0;
323 
324  alog("%s: %s!%s@%s (level %d) %s access level %d to %s (group %s) on channel %s", s_ChanServ, u->nick, u->username, u->host, ulev, change ? "changed" : "set", access->level, na->nick, nc->display, ci->name);
325 
326  snprintf(event_access, BUFSIZE, "%d", access->level);
327 
328  if (!change) {
329  send_event(EVENT_ACCESS_ADD, 4, ci->name, u->nick, na->nick,
330  event_access);
331  notice_lang(s_ChanServ, u, xmsgs[3], access->nc->display,
332  ci->name);
333  } else {
334  send_event(EVENT_ACCESS_CHANGE, 4, ci->name, u->nick, na->nick,
335  event_access);
336  notice_lang(s_ChanServ, u, xmsgs[4], access->nc->display,
337  ci->name);
338  }
339 
340  } else if (stricmp(cmd, "DEL") == 0) {
341  int deleted;
342  if (readonly) {
343  notice_lang(s_ChanServ, u, xmsgs[1]);
344  return MOD_CONT;
345  }
346 
347  if (ci->accesscount == 0) {
348  notice_lang(s_ChanServ, u, xmsgs[11], chan);
349  return MOD_CONT;
350  }
351 
352  ulev = get_access(u, ci);
353 
354  if ((xlev >= ulev || ulev < ACCESS_AOP) && !is_servadmin) {
355  notice_lang(s_ChanServ, u, PERMISSION_DENIED);
356  return MOD_CONT;
357  }
358 
359  /* Special case: is it a number/list? Only do search if it isn't. */
360  if (isdigit(*nick) && strspn(nick, "1234567890,-") == strlen(nick)) {
361  int count, last = -1, perm = 0;
362  deleted =
363  process_numlist(nick, &count, xop_del_callback, u, ci,
364  &last, &perm, ulev, xlev);
365  if (!deleted) {
366  if (perm) {
367  notice_lang(s_ChanServ, u, PERMISSION_DENIED);
368  } else if (count == 1) {
369  last = atoi(nick);
370  notice_lang(s_ChanServ, u, xmsgs[5], last, ci->name);
371  } else {
372  notice_lang(s_ChanServ, u, xmsgs[7], ci->name);
373  }
374  } else if (deleted == 1) {
375  alog("%s: %s!%s@%s (level %d) deleted access of user %s on %s", s_ChanServ, u->nick, u->username, u->host, get_access(u, ci), nick, ci->name);
376  notice_lang(s_ChanServ, u, xmsgs[9], ci->name);
377  } else {
378  alog("%s: %s!%s@%s (level %d) deleted access of users %s on %s", s_ChanServ, u->nick, u->username, u->host, get_access(u, ci), nick, ci->name);
379  notice_lang(s_ChanServ, u, xmsgs[10], deleted, ci->name);
380  }
381  } else {
382  na = findnick(nick);
383  if (!na) {
384  notice_lang(s_ChanServ, u, NICK_X_NOT_REGISTERED, nick);
385  return MOD_CONT;
386  }
387  nc = na->nc;
388 
389  for (i = 0; i < ci->accesscount; i++)
390  if (ci->access[i].nc == nc && ci->access[i].level == xlev)
391  break;
392 
393  if (i == ci->accesscount) {
394  notice_lang(s_ChanServ, u, xmsgs[6], nick, chan);
395  return MOD_CONT;
396  }
397 
398  access = &ci->access[i];
399  if (!is_servadmin && ulev <= access->level) {
400  deleted = 0;
401  notice_lang(s_ChanServ, u, PERMISSION_DENIED);
402  } else {
403  alog("%s: %s!%s@%s (level %d) deleted access of %s on %s", s_ChanServ, u->nick, u->username, u->host, get_access(u, ci), access->nc->display, ci->name);
404  notice_lang(s_ChanServ, u, xmsgs[8], access->nc->display,
405  ci->name);
406  access->nc = NULL;
407  access->in_use = 0;
408  send_event(EVENT_ACCESS_DEL, 3, ci->name, u->nick,
409  na->nick);
410  deleted = 1;
411  }
412  }
413  if (deleted)
414  CleanAccess(ci);
415  } else if (stricmp(cmd, "LIST") == 0) {
416  int sent_header = 0;
417 
418  ulev = get_access(u, ci);
419 
420  if (!is_servadmin && ulev < ACCESS_AOP) {
421  notice_lang(s_ChanServ, u, ACCESS_DENIED);
422  return MOD_CONT;
423  }
424 
425  if (ci->accesscount == 0) {
426  notice_lang(s_ChanServ, u, xmsgs[11], ci->name);
427  return MOD_CONT;
428  }
429 
430  if (nick && strspn(nick, "1234567890,-") == strlen(nick)) {
431  process_numlist(nick, NULL, xop_list_callback, u, ci,
432  &sent_header, xlev, xmsgs[12]);
433  } else {
434  for (i = 0; i < ci->accesscount; i++) {
435  if (nick && ci->access[i].nc
436  && !match_wild_nocase(nick, ci->access[i].nc->display))
437  continue;
438  xop_list(u, i, ci, &sent_header, xlev, xmsgs[12]);
439  }
440  }
441  if (!sent_header)
442  notice_lang(s_ChanServ, u, xmsgs[7], chan);
443  } else if (stricmp(cmd, "CLEAR") == 0) {
444  if (readonly) {
445  notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
446  return MOD_CONT;
447  }
448 
449  if (ci->accesscount == 0) {
450  notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_EMPTY, chan);
451  return MOD_CONT;
452  }
453 
454  if (!is_servadmin && !is_founder(u, ci)) {
455  notice_lang(s_ChanServ, u, PERMISSION_DENIED);
456  return MOD_CONT;
457  }
458 
459  for (i = 0; i < ci->accesscount; i++) {
460  if (ci->access[i].in_use && ci->access[i].level == xlev) {
461  ci->access[i].nc = NULL;
462  ci->access[i].in_use = 0;
463  }
464  }
465 
466  CleanAccess(ci);
467 
468  alog("%s: %s!%s@%s cleared the %s list of %s", s_ChanServ, u->nick, u->username, u->host, xname, ci->name);
469 
470  send_event(EVENT_ACCESS_CLEAR, 2, ci->name, u->nick);
471 
472  notice_lang(s_ChanServ, u, xmsgs[13], ci->name);
473  } else {
474  syntax_error(s_ChanServ, u, xname, xmsgs[0]);
475  }
476  return MOD_CONT;
477 }
E size_t strspn(const char *s, const char *accept)
Definition: compat.c:106
E int process_numlist(const char *numstr, int *count_ret, range_callback_t callback, User *u,...)
Definition: misc.c:292
static int xop_msgs[4][14]
Definition: cs_xop.c:25
E int readonly
Definition: extern.h:776
E int match_wild_nocase(const char *pattern, const char *str)
Definition: misc.c:268
char nick[NICKMAX]
Definition: services.h:875
E int snprintf(char *buf, size_t size, const char *fmt,...)
Definition: compat.c:37
E NickAlias * findnick(const char *nick)
Definition: db-merger.c:1857
E IRCDVar * ircd
Definition: extern.h:39
E void send_event(const char *name, int argc,...)
Definition: events.c:37
int AnopeInit(int argc, char **argv)
Definition: cs_xop.c:90
E int stricmp(const char *s1, const char *s2)
Definition: compat.c:58
int16 level
Definition: services.h:594
#define NS_VERBOTEN
Definition: services.h:1273
#define CI_VERBOTEN
Definition: services.h:725
char * host
Definition: services.h:878
#define ACCESS_VOP
Definition: services.h:614
static int do_sop(User *u)
Definition: cs_xop.c:157
static int xop_del(User *u, ChannelInfo *ci, ChanAccess *access, int *perm, int uacc, int xlev)
Definition: cs_xop.c:173
MDE void moduleAddAuthor(const char *author)
Definition: modules.c:1772
E void syntax_error(char *service, User *u, const char *command, int msgnum)
Definition: language.c:295
char name[CHANMAX]
Definition: services.h:654
static int xop_del_callback(User *u, int num, va_list args)
Definition: cs_xop.c:189
E void notice_lang(char *source, User *dest, int message,...)
Definition: send.c:169
MDE void moduleSetType(MODType type)
Definition: modules.c:818
NickCore * nc
Definition: services.h:533
time_t last_seen
Definition: services.h:596
#define ACCESS_SOP
Definition: services.h:617
char * display
Definition: services.h:542
uint32 flags
Definition: services.h:669
uint16 status
Definition: services.h:532
MDE void moduleAddVersion(const char *version)
Definition: modules.c:1760
uint16 accesscount
Definition: services.h:676
static int do_vop(User *u)
Definition: cs_xop.c:164
#define EVENT_ACCESS_DEL
Definition: events.h:65
MDE void moduleSetChanHelp(void(*func)(User *u))
Definition: modules.c:2090
#define EVENT_ACCESS_CHANGE
Definition: events.h:64
static int xop_list_callback(User *u, int num, va_list args)
Definition: cs_xop.c:223
Command * c
Definition: ns_recover.c:17
#define CHANSERV
Definition: modules.h:60
E void alog(const char *fmt,...) FORMAT(printf
#define CI_XOP
Definition: services.h:741
#define MOD_CONT
Definition: modules.h:54
NickCore * nc
Definition: services.h:595
E void CleanAccess(ChannelInfo *ci)
Definition: chanserv.c:2755
E int is_founder(User *user, ChannelInfo *ci)
Definition: chanserv.c:2255
E int is_services_admin(User *u)
Definition: operserv.c:591
static void myChanServHelp(User *u)
Definition: cs_xop.c:126
#define ACCESS_HOP
Definition: services.h:615
char * nick
Definition: services.h:526
char * username
Definition: services.h:877
static int do_aop(User *u)
Definition: cs_xop.c:141
Definition: modules.h:99
MDE Command * createCommand(const char *name, int(*func)(User *u), int(*has_priv)(User *u), int help_all, int help_reg, int help_oper, int help_admin, int help_root)
Definition: modules.c:987
#define EVENT_ACCESS_ADD
Definition: events.h:63
static int xop_list(User *u, int index, ChannelInfo *ci, int *sent_header, int xlev, int xmsg)
Definition: cs_xop.c:205
E int CSAccessMax
Definition: extern.h:406
E ChannelInfo * cs_findchan(const char *chan)
Definition: db-merger.c:2000
E void * srealloc(void *oldptr, long newsize)
Definition: memory.c:80
static int do_hop(User *u)
Definition: cs_xop.c:148
ChanAccess * access
Definition: services.h:677
E char * s_ChanServ
Definition: extern.h:285
E int get_access(User *user, ChannelInfo *ci)
Definition: chanserv.c:2332
uint16 in_use
Definition: services.h:593
MDE int moduleAddCommand(CommandHash *cmdTable[], Command *c, int pos)
Definition: modules.c:1082
int halfop
Definition: services.h:316
static int do_xop(User *u, char *xname, int xlev, int *xmsgs)
Definition: cs_xop.c:237
#define ACCESS_AOP
Definition: services.h:616
#define BUFSIZE
Definition: config.h:47
void AnopeFini(void)
Definition: cs_xop.c:115
#define EVENT_ACCESS_CLEAR
Definition: events.h:66
#define MOD_UNIQUE
Definition: module.h:11