Anope IRC Services  Version 1.8
cs_access.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 
18 static int do_access(User * u);
19 static int do_levels(User * u);
20 
21 static void myChanServHelp(User * u);
22 
29 int AnopeInit(int argc, char **argv)
30 {
31  Command *c;
32 
33  moduleAddAuthor("Anope");
35  (VERSION_STRING);
37 
38  c = createCommand("ACCESS", do_access, NULL, CHAN_HELP_ACCESS, -1, -1,
39  -1, -1);
41  c = createCommand("ACCESS LEVELS", NULL, NULL, CHAN_HELP_ACCESS_LEVELS,
42  -1, -1, -1, -1);
44  c = createCommand("LEVELS", do_levels, NULL, CHAN_HELP_LEVELS, -1, -1,
45  -1, -1);
48 
49  return MOD_CONT;
50 }
51 
55 void AnopeFini(void)
56 {
57 
58 }
59 
60 
61 
66 static void myChanServHelp(User * u)
67 {
68  notice_lang(s_ChanServ, u, CHAN_HELP_CMD_ACCESS);
69  notice_lang(s_ChanServ, u, CHAN_HELP_CMD_LEVELS);
70 }
71 
72 
73 static int access_del(User * u, ChannelInfo *ci, ChanAccess * access, int *perm, int uacc)
74 {
75  char *nick;
76  if (!access->in_use)
77  return 0;
78  if (!is_services_admin(u) && uacc <= access->level) {
79  (*perm)++;
80  return 0;
81  }
82  nick = access->nc->display;
83  access->nc = NULL;
84  access->in_use = 0;
85  send_event(EVENT_ACCESS_DEL, 3, ci->name, u->nick, nick);
86  return 1;
87 }
88 
89 static int access_del_callback(User * u, int num, va_list args)
90 {
91  ChannelInfo *ci = va_arg(args, ChannelInfo *);
92  int *last = va_arg(args, int *);
93  int *perm = va_arg(args, int *);
94  int uacc = va_arg(args, int);
95  if (num < 1 || num > ci->accesscount)
96  return 0;
97  *last = num;
98  return access_del(u, ci, &ci->access[num - 1], perm, uacc);
99 }
100 
101 
102 static int access_list(User * u, int index, ChannelInfo * ci,
103  int *sent_header)
104 {
105  ChanAccess *access = &ci->access[index];
106  const char *xop;
107 
108  if (!access->in_use)
109  return 0;
110 
111  if (!*sent_header) {
112  notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_HEADER, ci->name);
113  *sent_header = 1;
114  }
115 
116  if (ci->flags & CI_XOP) {
117  xop = get_xop_level(access->level);
118  notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_XOP_FORMAT, index + 1,
119  xop, access->nc->display);
120  } else {
121  notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_AXS_FORMAT, index + 1,
122  access->level, access->nc->display);
123  }
124  return 1;
125 }
126 
127 static int access_list_callback(User * u, int num, va_list args)
128 {
129  ChannelInfo *ci = va_arg(args, ChannelInfo *);
130  int *sent_header = va_arg(args, int *);
131  if (num < 1 || num > ci->accesscount)
132  return 0;
133  return access_list(u, num - 1, ci, sent_header);
134 }
135 
136 
142 static int do_access(User * u)
143 {
144  char *chan = strtok(NULL, " ");
145  char *cmd = strtok(NULL, " ");
146  char *nick = strtok(NULL, " ");
147  char *s = strtok(NULL, " ");
148  char event_access[BUFSIZE];
149 
150  ChannelInfo *ci;
151  NickAlias *na = NULL;
152  NickCore *nc;
153  ChanAccess *access;
154 
155  int i;
156  int level = 0, ulev;
157  int is_clear = (cmd && stricmp(cmd, "CLEAR") == 0);
158  int is_list = (cmd && stricmp(cmd, "LIST") == 0);
159  int is_servadmin = is_services_admin(u);
160 
161  /* If LIST, we don't *require* any parameters, but we can take any.
162  * If DEL, we require a nick and no level.
163  * Else (ADD), we require a level (which implies a nick). */
164  if (!cmd || ((is_list || is_clear) ? 0 :
165  (stricmp(cmd, "DEL") == 0) ? (!nick || s) : !s)) {
166  syntax_error(s_ChanServ, u, "ACCESS", CHAN_ACCESS_SYNTAX);
167  } else if (!(ci = cs_findchan(chan))) {
168  notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
169  } else if (ci->flags & CI_VERBOTEN) {
170  notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
171  /* We still allow LIST and CLEAR in xOP mode, but not others */
172  } else if ((ci->flags & CI_XOP) && !is_list && !is_clear) {
173  if (ircd->halfop)
174  notice_lang(s_ChanServ, u, CHAN_ACCESS_XOP_HOP, s_ChanServ);
175  else
176  notice_lang(s_ChanServ, u, CHAN_ACCESS_XOP, s_ChanServ);
177  } else if (((is_list && !check_access(u, ci, CA_ACCESS_LIST))
178  || (!is_list && !check_access(u, ci, CA_ACCESS_CHANGE)))
179  && !is_servadmin) {
180  notice_lang(s_ChanServ, u, ACCESS_DENIED);
181  } else if (stricmp(cmd, "ADD") == 0) {
182  if (readonly) {
183  notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
184  return MOD_CONT;
185  }
186 
187  level = atoi(s);
188  ulev = get_access(u, ci);
189 
190  if (!is_servadmin && level >= ulev) {
191  notice_lang(s_ChanServ, u, PERMISSION_DENIED);
192  return MOD_CONT;
193  }
194 
195  if (level == 0) {
196  notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_NONZERO);
197  return MOD_CONT;
198  } else if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER) {
199  notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_RANGE,
200  ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
201  return MOD_CONT;
202  }
203 
204  na = findnick(nick);
205  if (!na) {
206  notice_lang(s_ChanServ, u, CHAN_ACCESS_NICKS_ONLY);
207  return MOD_CONT;
208  }
209  if (na->status & NS_VERBOTEN) {
210  notice_lang(s_ChanServ, u, NICK_X_FORBIDDEN, nick);
211  return MOD_CONT;
212  }
213 
214  nc = na->nc;
215  for (access = ci->access, i = 0; i < ci->accesscount;
216  access++, i++) {
217  if (access->nc == nc) {
218  /* Don't allow lowering from a level >= ulev */
219  if (!is_servadmin && access->level >= ulev) {
220  notice_lang(s_ChanServ, u, PERMISSION_DENIED);
221  return MOD_CONT;
222  }
223  if (access->level == level) {
224  notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_UNCHANGED,
225  access->nc->display, chan, level);
226  return MOD_CONT;
227  }
228  access->level = level;
229  snprintf(event_access, BUFSIZE, "%d", access->level);
231  na->nick, event_access);
232  alog("%s: %s!%s@%s (level %d) set access level %d to %s (group %s) on channel %s", s_ChanServ, u->nick, u->username, u->host, ulev, access->level, na->nick, nc->display, ci->name);
233  notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_CHANGED,
234  access->nc->display, chan, level);
235  return MOD_CONT;
236  }
237  }
238 
239  if (i < CSAccessMax) {
240  ci->accesscount++;
241  ci->access =
242  srealloc(ci->access,
243  sizeof(ChanAccess) * ci->accesscount);
244  } else {
245  notice_lang(s_ChanServ, u, CHAN_ACCESS_REACHED_LIMIT,
246  CSAccessMax);
247  return MOD_CONT;
248  }
249 
250  access = &ci->access[i];
251  access->nc = nc;
252  access->in_use = 1;
253  access->level = level;
254  access->last_seen = 0;
255 
256  snprintf(event_access, BUFSIZE, "%d", access->level);
257  send_event(EVENT_ACCESS_ADD, 4, ci->name, u->nick, na->nick,
258  event_access);
259  alog("%s: %s!%s@%s (level %d) set access level %d to %s (group %s) on channel %s", s_ChanServ, u->nick, u->username, u->host, ulev, access->level, na->nick, nc->display, ci->name);
260  notice_lang(s_ChanServ, u, CHAN_ACCESS_ADDED, nc->display,
261  ci->name, access->level);
262  } else if (stricmp(cmd, "DEL") == 0) {
263  int deleted;
264 
265  if (readonly) {
266  notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
267  return MOD_CONT;
268  }
269 
270  if (ci->accesscount == 0) {
271  notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_EMPTY, chan);
272  return MOD_CONT;
273  }
274 
275  /* Clean the access list to make sure every thing is in use */
276  CleanAccess(ci);
277 
278  /* Special case: is it a number/list? Only do search if it isn't. */
279  if (isdigit(*nick) && strspn(nick, "1234567890,-") == strlen(nick)) {
280  int count, last = -1, perm = 0;
281  deleted = process_numlist(nick, &count, access_del_callback, u,
282  ci, &last, &perm, get_access(u, ci));
283  if (!deleted) {
284  if (perm) {
285  notice_lang(s_ChanServ, u, PERMISSION_DENIED);
286  } else if (count == 1) {
287  last = atoi(nick);
288  notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_SUCH_ENTRY,
289  last, ci->name);
290  } else {
291  notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_MATCH,
292  ci->name);
293  }
294  } else {
295  alog("%s: %s!%s@%s (level %d) deleted access of user%s %s on %s",
296  s_ChanServ, u->nick, u->username, u->host, get_access(u, ci), (deleted == 1 ? "" : "s"), nick, chan);
297  if (deleted == 1)
298  notice_lang(s_ChanServ, u, CHAN_ACCESS_DELETED_ONE,
299  ci->name);
300  else
301  notice_lang(s_ChanServ, u, CHAN_ACCESS_DELETED_SEVERAL,
302  deleted, ci->name);
303  }
304  } else {
305  na = findnick(nick);
306  if (!na) {
307  notice_lang(s_ChanServ, u, NICK_X_NOT_REGISTERED, nick);
308  return MOD_CONT;
309  }
310  nc = na->nc;
311  for (i = 0; i < ci->accesscount; i++) {
312  if (ci->access[i].nc == nc)
313  break;
314  }
315  if (i == ci->accesscount) {
316  notice_lang(s_ChanServ, u, CHAN_ACCESS_NOT_FOUND, nick,
317  chan);
318  return MOD_CONT;
319  }
320  access = &ci->access[i];
321  if (!is_servadmin && get_access(u, ci) <= access->level) {
322  deleted = 0;
323  notice_lang(s_ChanServ, u, PERMISSION_DENIED);
324  } else {
325  notice_lang(s_ChanServ, u, CHAN_ACCESS_DELETED,
326  access->nc->display, ci->name);
327  alog("%s: %s!%s@%s (level %d) deleted access of %s (group %s) on %s", s_ChanServ, u->nick, u->username, u->host, get_access(u, ci), na->nick, access->nc->display, chan);
328  access->nc = NULL;
329  access->in_use = 0;
330  deleted = 1;
331  }
332  }
333 
334  CleanAccess(ci);
335 
336  /* We don't know the nick if someone used numbers, so we trigger the event without
337  * nick param. We just do this once, even if someone enters a range. -Certus */
338  if (na)
339  send_event(EVENT_ACCESS_DEL, 3, ci->name, u->nick, na->nick);
340  else
341  send_event(EVENT_ACCESS_DEL, 2, ci->name, u->nick);
342  } else if (stricmp(cmd, "LIST") == 0) {
343  int sent_header = 0;
344 
345  if (ci->accesscount == 0) {
346  notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_EMPTY, chan);
347  return MOD_CONT;
348  }
349 
350  CleanAccess(ci);
351 
352  if (nick && strspn(nick, "1234567890,-") == strlen(nick)) {
353  process_numlist(nick, NULL, access_list_callback, u, ci,
354  &sent_header);
355  } else {
356  for (i = 0; i < ci->accesscount; i++) {
357  if (nick && ci->access[i].nc
358  && !match_wild_nocase(nick, ci->access[i].nc->display))
359  continue;
360  access_list(u, i, ci, &sent_header);
361  }
362  }
363  if (!sent_header) {
364  notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_MATCH, chan);
365  } else {
366  notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_FOOTER, ci->name);
367  }
368  } else if (stricmp(cmd, "CLEAR") == 0) {
369 
370  if (readonly) {
371  notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
372  return MOD_CONT;
373  }
374 
375  if (!is_servadmin && !is_founder(u, ci)) {
376  notice_lang(s_ChanServ, u, PERMISSION_DENIED);
377  return MOD_CONT;
378  }
379 
380  free(ci->access);
381  ci->access = NULL;
382  ci->accesscount = 0;
383 
384  send_event(EVENT_ACCESS_CLEAR, 2, ci->name, u->nick);
385 
386  notice_lang(s_ChanServ, u, CHAN_ACCESS_CLEAR, ci->name);
387  alog("%s: %s!%s@%s (level %d) cleared access list on %s",
388  s_ChanServ, u->nick, u->username, u->host,
389  get_access(u, ci), chan);
390 
391  } else {
392  syntax_error(s_ChanServ, u, "ACCESS", CHAN_ACCESS_SYNTAX);
393  }
394  return MOD_CONT;
395 }
396 
397 
398 static int do_levels(User * u)
399 {
400  char *chan = strtok(NULL, " ");
401  char *cmd = strtok(NULL, " ");
402  char *what = strtok(NULL, " ");
403  char *s = strtok(NULL, " ");
404  char *error;
405 
406  ChannelInfo *ci;
407  int level;
408  int i;
409 
410  /* If SET, we want two extra parameters; if DIS[ABLE] or FOUNDER, we want only
411  * one; else, we want none.
412  */
413  if (!cmd
414  || ((stricmp(cmd, "SET") == 0) ? !s
415  : ((strnicmp(cmd, "DIS", 3) == 0)) ? (!what || s) : !!what)) {
416  syntax_error(s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
417  } else if (!(ci = cs_findchan(chan))) {
418  notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
419  } else if (ci->flags & CI_VERBOTEN) {
420  notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
421  } else if (ci->flags & CI_XOP) {
422  notice_lang(s_ChanServ, u, CHAN_LEVELS_XOP);
423  } else if (!is_founder(u, ci) && !is_services_admin(u)) {
424  notice_lang(s_ChanServ, u, ACCESS_DENIED);
425  } else if (stricmp(cmd, "SET") == 0) {
426  level = strtol(s, &error, 10);
427 
428  if (*error != '\0') {
429  syntax_error(s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
430  return MOD_CONT;
431  }
432 
433  if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER) {
434  notice_lang(s_ChanServ, u, CHAN_LEVELS_RANGE,
435  ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
436  return MOD_CONT;
437  }
438 
439  for (i = 0; levelinfo[i].what >= 0; i++) {
440  if (stricmp(levelinfo[i].name, what) == 0) {
441  ci->levels[levelinfo[i].what] = level;
442 
443  alog("%s: %s!%s@%s set level %s on channel %s to %d",
444  s_ChanServ, u->nick, u->username, u->host,
445  levelinfo[i].name, ci->name, level);
446  notice_lang(s_ChanServ, u, CHAN_LEVELS_CHANGED,
447  levelinfo[i].name, chan, level);
448  return MOD_CONT;
449  }
450  }
451 
452  notice_lang(s_ChanServ, u, CHAN_LEVELS_UNKNOWN, what, s_ChanServ);
453 
454  } else if (stricmp(cmd, "DIS") == 0 || stricmp(cmd, "DISABLE") == 0) {
455  for (i = 0; levelinfo[i].what >= 0; i++) {
456  if (stricmp(levelinfo[i].name, what) == 0) {
458 
459  alog("%s: %s!%s@%s disabled level %s on channel %s",
460  s_ChanServ, u->nick, u->username, u->host,
461  levelinfo[i].name, ci->name);
462  notice_lang(s_ChanServ, u, CHAN_LEVELS_DISABLED,
463  levelinfo[i].name, chan);
464  return MOD_CONT;
465  }
466  }
467 
468  notice_lang(s_ChanServ, u, CHAN_LEVELS_UNKNOWN, what, s_ChanServ);
469  } else if (stricmp(cmd, "LIST") == 0) {
470  int i;
471 
472  notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_HEADER, chan);
473 
474  if (!levelinfo_maxwidth) {
475  for (i = 0; levelinfo[i].what >= 0; i++) {
476  int len = strlen(levelinfo[i].name);
477  if (len > levelinfo_maxwidth)
478  levelinfo_maxwidth = len;
479  }
480  }
481 
482  for (i = 0; levelinfo[i].what >= 0; i++) {
483  int j = ci->levels[levelinfo[i].what];
484 
485  if (j == ACCESS_INVALID) {
486  j = levelinfo[i].what;
487 
488  if (j == CA_AUTOOP || j == CA_AUTODEOP || j == CA_AUTOVOICE
489  || j == CA_NOJOIN) {
490  notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_DISABLED,
491  levelinfo_maxwidth, levelinfo[i].name);
492  } else {
493  notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_DISABLED,
494  levelinfo_maxwidth, levelinfo[i].name);
495  }
496  } else if (j == ACCESS_FOUNDER) {
497  notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_FOUNDER,
498  levelinfo_maxwidth, levelinfo[i].name);
499  } else {
500  notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_NORMAL,
501  levelinfo_maxwidth, levelinfo[i].name, j);
502  }
503  }
504 
505  } else if (stricmp(cmd, "RESET") == 0) {
506  reset_levels(ci);
507 
508  alog("%s: %s!%s@%s reset levels definitions on channel %s",
509  s_ChanServ, u->nick, u->username, u->host, ci->name);
510  notice_lang(s_ChanServ, u, CHAN_LEVELS_RESET, chan);
511  } else {
512  syntax_error(s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
513  }
514  return MOD_CONT;
515 }
#define CA_AUTOVOICE
Definition: services.h:755
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
#define CA_AUTOOP
Definition: services.h:753
E int readonly
Definition: extern.h:776
#define CA_ACCESS_CHANGE
Definition: services.h:760
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 int strnicmp(const char *s1, const char *s2, size_t len)
Definition: compat.c:73
E int check_access(User *user, ChannelInfo *ci, int what)
Definition: chanserv.c:1974
E void send_event(const char *name, int argc,...)
Definition: events.c:37
static int access_del_callback(User *u, int num, va_list args)
Definition: cs_access.c:89
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 CA_ACCESS_LIST
Definition: services.h:757
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
#define CA_AUTODEOP
Definition: services.h:754
#define CA_NOJOIN
Definition: services.h:759
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
char * display
Definition: services.h:542
char * name
Definition: services.h:836
#define ACCESS_FOUNDER
Definition: services.h:604
static int access_list(User *u, int index, ChannelInfo *ci, int *sent_header)
Definition: cs_access.c:102
#define ACCESS_INVALID
Definition: services.h:605
uint32 flags
Definition: services.h:669
uint16 status
Definition: services.h:532
static int access_list_callback(User *u, int num, va_list args)
Definition: cs_access.c:127
MDE void moduleAddVersion(const char *version)
Definition: modules.c:1760
uint16 accesscount
Definition: services.h:676
static int do_levels(User *u)
Definition: cs_access.c:398
int what
Definition: services.h:835
#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
int AnopeInit(int argc, char **argv)
Definition: cs_access.c:29
static int access_del(User *u, ChannelInfo *ci, ChanAccess *access, int *perm, int uacc)
Definition: cs_access.c:73
Command * c
Definition: ns_recover.c:17
static void myChanServHelp(User *u)
Definition: cs_access.c:66
#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 reset_levels(ChannelInfo *ci)
Definition: chanserv.c:2233
E LevelInfo levelinfo[]
Definition: extern.h:169
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
char * nick
Definition: services.h:526
char * username
Definition: services.h:877
Definition: modules.h:99
static int do_access(User *u)
Definition: cs_access.c:142
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
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
void AnopeFini(void)
Definition: cs_access.c:55
E int levelinfo_maxwidth
Definition: extern.h:224
ChanAccess * access
Definition: services.h:677
void error(int linenum, const char *message,...)
Definition: config.c:648
int16 * levels
Definition: services.h:674
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
E const char * get_xop_level(int level)
Definition: chanserv.c:2628
#define BUFSIZE
Definition: config.h:47
#define EVENT_ACCESS_CLEAR
Definition: events.h:66
#define MOD_UNIQUE
Definition: module.h:11