00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "module.h"
00016
00017
00018 static int do_access(User * u);
00019 static int do_levels(User * u);
00020
00021 static void myChanServHelp(User * u);
00022
00029 int AnopeInit(int argc, char **argv)
00030 {
00031 Command *c;
00032
00033 moduleAddAuthor("Anope");
00034 moduleAddVersion
00035 (VERSION_STRING);
00036 moduleSetType(CORE);
00037
00038 c = createCommand("ACCESS", do_access, NULL, CHAN_HELP_ACCESS, -1, -1,
00039 -1, -1);
00040 moduleAddCommand(CHANSERV, c, MOD_UNIQUE);
00041 c = createCommand("ACCESS LEVELS", NULL, NULL, CHAN_HELP_ACCESS_LEVELS,
00042 -1, -1, -1, -1);
00043 moduleAddCommand(CHANSERV, c, MOD_UNIQUE);
00044 c = createCommand("LEVELS", do_levels, NULL, CHAN_HELP_LEVELS, -1, -1,
00045 -1, -1);
00046 moduleAddCommand(CHANSERV, c, MOD_UNIQUE);
00047 moduleSetChanHelp(myChanServHelp);
00048
00049 return MOD_CONT;
00050 }
00051
00055 void AnopeFini(void)
00056 {
00057
00058 }
00059
00060
00061
00066 static void myChanServHelp(User * u)
00067 {
00068 notice_lang(s_ChanServ, u, CHAN_HELP_CMD_ACCESS);
00069 notice_lang(s_ChanServ, u, CHAN_HELP_CMD_LEVELS);
00070 }
00071
00072
00073 static int access_del(User * u, ChannelInfo *ci, ChanAccess * access, int *perm, int uacc)
00074 {
00075 char *nick;
00076 if (!access->in_use)
00077 return 0;
00078 if (!is_services_admin(u) && uacc <= access->level) {
00079 (*perm)++;
00080 return 0;
00081 }
00082 nick = access->nc->display;
00083 access->nc = NULL;
00084 access->in_use = 0;
00085 send_event(EVENT_ACCESS_DEL, 3, ci->name, u->nick, nick);
00086 return 1;
00087 }
00088
00089 static int access_del_callback(User * u, int num, va_list args)
00090 {
00091 ChannelInfo *ci = va_arg(args, ChannelInfo *);
00092 int *last = va_arg(args, int *);
00093 int *perm = va_arg(args, int *);
00094 int uacc = va_arg(args, int);
00095 if (num < 1 || num > ci->accesscount)
00096 return 0;
00097 *last = num;
00098 return access_del(u, ci, &ci->access[num - 1], perm, uacc);
00099 }
00100
00101
00102 static int access_list(User * u, int index, ChannelInfo * ci,
00103 int *sent_header)
00104 {
00105 ChanAccess *access = &ci->access[index];
00106 const char *xop;
00107
00108 if (!access->in_use)
00109 return 0;
00110
00111 if (!*sent_header) {
00112 notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_HEADER, ci->name);
00113 *sent_header = 1;
00114 }
00115
00116 if (ci->flags & CI_XOP) {
00117 xop = get_xop_level(access->level);
00118 notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_XOP_FORMAT, index + 1,
00119 xop, access->nc->display);
00120 } else {
00121 notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_AXS_FORMAT, index + 1,
00122 access->level, access->nc->display);
00123 }
00124 return 1;
00125 }
00126
00127 static int access_list_callback(User * u, int num, va_list args)
00128 {
00129 ChannelInfo *ci = va_arg(args, ChannelInfo *);
00130 int *sent_header = va_arg(args, int *);
00131 if (num < 1 || num > ci->accesscount)
00132 return 0;
00133 return access_list(u, num - 1, ci, sent_header);
00134 }
00135
00136
00142 static int do_access(User * u)
00143 {
00144 char *chan = strtok(NULL, " ");
00145 char *cmd = strtok(NULL, " ");
00146 char *nick = strtok(NULL, " ");
00147 char *s = strtok(NULL, " ");
00148 char event_access[BUFSIZE];
00149
00150 ChannelInfo *ci;
00151 NickAlias *na = NULL;
00152 NickCore *nc;
00153 ChanAccess *access;
00154
00155 int i;
00156 int level = 0, ulev;
00157 int is_clear = (cmd && stricmp(cmd, "CLEAR") == 0);
00158 int is_list = (cmd && stricmp(cmd, "LIST") == 0);
00159 int is_servadmin = is_services_admin(u);
00160
00161
00162
00163
00164 if (!cmd || ((is_list || is_clear) ? 0 :
00165 (stricmp(cmd, "DEL") == 0) ? (!nick || s) : !s)) {
00166 syntax_error(s_ChanServ, u, "ACCESS", CHAN_ACCESS_SYNTAX);
00167 } else if (!(ci = cs_findchan(chan))) {
00168 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
00169 } else if (ci->flags & CI_VERBOTEN) {
00170 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
00171
00172 } else if ((ci->flags & CI_XOP) && !is_list && !is_clear) {
00173 if (ircd->halfop)
00174 notice_lang(s_ChanServ, u, CHAN_ACCESS_XOP_HOP, s_ChanServ);
00175 else
00176 notice_lang(s_ChanServ, u, CHAN_ACCESS_XOP, s_ChanServ);
00177 } else if (((is_list && !check_access(u, ci, CA_ACCESS_LIST))
00178 || (!is_list && !check_access(u, ci, CA_ACCESS_CHANGE)))
00179 && !is_servadmin) {
00180 notice_lang(s_ChanServ, u, ACCESS_DENIED);
00181 } else if (stricmp(cmd, "ADD") == 0) {
00182 if (readonly) {
00183 notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
00184 return MOD_CONT;
00185 }
00186
00187 level = atoi(s);
00188 ulev = get_access(u, ci);
00189
00190 if (!is_servadmin && level >= ulev) {
00191 notice_lang(s_ChanServ, u, PERMISSION_DENIED);
00192 return MOD_CONT;
00193 }
00194
00195 if (level == 0) {
00196 notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_NONZERO);
00197 return MOD_CONT;
00198 } else if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER) {
00199 notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_RANGE,
00200 ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
00201 return MOD_CONT;
00202 }
00203
00204 na = findnick(nick);
00205 if (!na) {
00206 notice_lang(s_ChanServ, u, CHAN_ACCESS_NICKS_ONLY);
00207 return MOD_CONT;
00208 }
00209 if (na->status & NS_VERBOTEN) {
00210 notice_lang(s_ChanServ, u, NICK_X_FORBIDDEN, nick);
00211 return MOD_CONT;
00212 }
00213
00214 nc = na->nc;
00215 for (access = ci->access, i = 0; i < ci->accesscount;
00216 access++, i++) {
00217 if (access->nc == nc) {
00218
00219 if (!is_servadmin && access->level >= ulev) {
00220 notice_lang(s_ChanServ, u, PERMISSION_DENIED);
00221 return MOD_CONT;
00222 }
00223 if (access->level == level) {
00224 notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_UNCHANGED,
00225 access->nc->display, chan, level);
00226 return MOD_CONT;
00227 }
00228 access->level = level;
00229 snprintf(event_access, BUFSIZE, "%d", access->level);
00230 send_event(EVENT_ACCESS_CHANGE, 4, ci->name, u->nick,
00231 na->nick, event_access);
00232 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);
00233 notice_lang(s_ChanServ, u, CHAN_ACCESS_LEVEL_CHANGED,
00234 access->nc->display, chan, level);
00235 return MOD_CONT;
00236 }
00237 }
00238
00239 if (i < CSAccessMax) {
00240 ci->accesscount++;
00241 ci->access =
00242 srealloc(ci->access,
00243 sizeof(ChanAccess) * ci->accesscount);
00244 } else {
00245 notice_lang(s_ChanServ, u, CHAN_ACCESS_REACHED_LIMIT,
00246 CSAccessMax);
00247 return MOD_CONT;
00248 }
00249
00250 access = &ci->access[i];
00251 access->nc = nc;
00252 access->in_use = 1;
00253 access->level = level;
00254 access->last_seen = 0;
00255
00256 snprintf(event_access, BUFSIZE, "%d", access->level);
00257 send_event(EVENT_ACCESS_ADD, 4, ci->name, u->nick, na->nick,
00258 event_access);
00259 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);
00260 notice_lang(s_ChanServ, u, CHAN_ACCESS_ADDED, nc->display,
00261 ci->name, access->level);
00262 } else if (stricmp(cmd, "DEL") == 0) {
00263 int deleted;
00264
00265 if (readonly) {
00266 notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
00267 return MOD_CONT;
00268 }
00269
00270 if (ci->accesscount == 0) {
00271 notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_EMPTY, chan);
00272 return MOD_CONT;
00273 }
00274
00275
00276 CleanAccess(ci);
00277
00278
00279 if (isdigit(*nick) && strspn(nick, "1234567890,-") == strlen(nick)) {
00280 int count, last = -1, perm = 0;
00281 deleted = process_numlist(nick, &count, access_del_callback, u,
00282 ci, &last, &perm, get_access(u, ci));
00283 if (!deleted) {
00284 if (perm) {
00285 notice_lang(s_ChanServ, u, PERMISSION_DENIED);
00286 } else if (count == 1) {
00287 last = atoi(nick);
00288 notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_SUCH_ENTRY,
00289 last, ci->name);
00290 } else {
00291 notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_MATCH,
00292 ci->name);
00293 }
00294 } else {
00295 alog("%s: %s!%s@%s (level %d) deleted access of user%s %s on %s",
00296 s_ChanServ, u->nick, u->username, u->host, get_access(u, ci), (deleted == 1 ? "" : "s"), nick, chan);
00297 if (deleted == 1)
00298 notice_lang(s_ChanServ, u, CHAN_ACCESS_DELETED_ONE,
00299 ci->name);
00300 else
00301 notice_lang(s_ChanServ, u, CHAN_ACCESS_DELETED_SEVERAL,
00302 deleted, ci->name);
00303 }
00304 } else {
00305 na = findnick(nick);
00306 if (!na) {
00307 notice_lang(s_ChanServ, u, NICK_X_NOT_REGISTERED, nick);
00308 return MOD_CONT;
00309 }
00310 nc = na->nc;
00311 for (i = 0; i < ci->accesscount; i++) {
00312 if (ci->access[i].nc == nc)
00313 break;
00314 }
00315 if (i == ci->accesscount) {
00316 notice_lang(s_ChanServ, u, CHAN_ACCESS_NOT_FOUND, nick,
00317 chan);
00318 return MOD_CONT;
00319 }
00320 access = &ci->access[i];
00321 if (!is_servadmin && get_access(u, ci) <= access->level) {
00322 deleted = 0;
00323 notice_lang(s_ChanServ, u, PERMISSION_DENIED);
00324 } else {
00325 notice_lang(s_ChanServ, u, CHAN_ACCESS_DELETED,
00326 access->nc->display, ci->name);
00327 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);
00328 access->nc = NULL;
00329 access->in_use = 0;
00330 deleted = 1;
00331 }
00332 }
00333
00334 CleanAccess(ci);
00335
00336
00337
00338 if (na)
00339 send_event(EVENT_ACCESS_DEL, 3, ci->name, u->nick, na->nick);
00340 else
00341 send_event(EVENT_ACCESS_DEL, 2, ci->name, u->nick);
00342 } else if (stricmp(cmd, "LIST") == 0) {
00343 int sent_header = 0;
00344
00345 if (ci->accesscount == 0) {
00346 notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_EMPTY, chan);
00347 return MOD_CONT;
00348 }
00349
00350 CleanAccess(ci);
00351
00352 if (nick && strspn(nick, "1234567890,-") == strlen(nick)) {
00353 process_numlist(nick, NULL, access_list_callback, u, ci,
00354 &sent_header);
00355 } else {
00356 for (i = 0; i < ci->accesscount; i++) {
00357 if (nick && ci->access[i].nc
00358 && !match_wild_nocase(nick, ci->access[i].nc->display))
00359 continue;
00360 access_list(u, i, ci, &sent_header);
00361 }
00362 }
00363 if (!sent_header) {
00364 notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_MATCH, chan);
00365 } else {
00366 notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_FOOTER, ci->name);
00367 }
00368 } else if (stricmp(cmd, "CLEAR") == 0) {
00369
00370 if (readonly) {
00371 notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
00372 return MOD_CONT;
00373 }
00374
00375 if (!is_servadmin && !is_founder(u, ci)) {
00376 notice_lang(s_ChanServ, u, PERMISSION_DENIED);
00377 return MOD_CONT;
00378 }
00379
00380 free(ci->access);
00381 ci->access = NULL;
00382 ci->accesscount = 0;
00383
00384 send_event(EVENT_ACCESS_CLEAR, 2, ci->name, u->nick);
00385
00386 notice_lang(s_ChanServ, u, CHAN_ACCESS_CLEAR, ci->name);
00387 alog("%s: %s!%s@%s (level %d) cleared access list on %s",
00388 s_ChanServ, u->nick, u->username, u->host,
00389 get_access(u, ci), chan);
00390
00391 } else {
00392 syntax_error(s_ChanServ, u, "ACCESS", CHAN_ACCESS_SYNTAX);
00393 }
00394 return MOD_CONT;
00395 }
00396
00397
00398 static int do_levels(User * u)
00399 {
00400 char *chan = strtok(NULL, " ");
00401 char *cmd = strtok(NULL, " ");
00402 char *what = strtok(NULL, " ");
00403 char *s = strtok(NULL, " ");
00404 char *error;
00405
00406 ChannelInfo *ci;
00407 int level;
00408 int i;
00409
00410
00411
00412
00413 if (!cmd
00414 || ((stricmp(cmd, "SET") == 0) ? !s
00415 : ((strnicmp(cmd, "DIS", 3) == 0)) ? (!what || s) : !!what)) {
00416 syntax_error(s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
00417 } else if (!(ci = cs_findchan(chan))) {
00418 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
00419 } else if (ci->flags & CI_VERBOTEN) {
00420 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
00421 } else if (ci->flags & CI_XOP) {
00422 notice_lang(s_ChanServ, u, CHAN_LEVELS_XOP);
00423 } else if (!is_founder(u, ci) && !is_services_admin(u)) {
00424 notice_lang(s_ChanServ, u, ACCESS_DENIED);
00425 } else if (stricmp(cmd, "SET") == 0) {
00426 level = strtol(s, &error, 10);
00427
00428 if (*error != '\0') {
00429 syntax_error(s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
00430 return MOD_CONT;
00431 }
00432
00433 if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER) {
00434 notice_lang(s_ChanServ, u, CHAN_LEVELS_RANGE,
00435 ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
00436 return MOD_CONT;
00437 }
00438
00439 for (i = 0; levelinfo[i].what >= 0; i++) {
00440 if (stricmp(levelinfo[i].name, what) == 0) {
00441 ci->levels[levelinfo[i].what] = level;
00442
00443 alog("%s: %s!%s@%s set level %s on channel %s to %d",
00444 s_ChanServ, u->nick, u->username, u->host,
00445 levelinfo[i].name, ci->name, level);
00446 notice_lang(s_ChanServ, u, CHAN_LEVELS_CHANGED,
00447 levelinfo[i].name, chan, level);
00448 return MOD_CONT;
00449 }
00450 }
00451
00452 notice_lang(s_ChanServ, u, CHAN_LEVELS_UNKNOWN, what, s_ChanServ);
00453
00454 } else if (stricmp(cmd, "DIS") == 0 || stricmp(cmd, "DISABLE") == 0) {
00455 for (i = 0; levelinfo[i].what >= 0; i++) {
00456 if (stricmp(levelinfo[i].name, what) == 0) {
00457 ci->levels[levelinfo[i].what] = ACCESS_INVALID;
00458
00459 alog("%s: %s!%s@%s disabled level %s on channel %s",
00460 s_ChanServ, u->nick, u->username, u->host,
00461 levelinfo[i].name, ci->name);
00462 notice_lang(s_ChanServ, u, CHAN_LEVELS_DISABLED,
00463 levelinfo[i].name, chan);
00464 return MOD_CONT;
00465 }
00466 }
00467
00468 notice_lang(s_ChanServ, u, CHAN_LEVELS_UNKNOWN, what, s_ChanServ);
00469 } else if (stricmp(cmd, "LIST") == 0) {
00470 int i;
00471
00472 notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_HEADER, chan);
00473
00474 if (!levelinfo_maxwidth) {
00475 for (i = 0; levelinfo[i].what >= 0; i++) {
00476 int len = strlen(levelinfo[i].name);
00477 if (len > levelinfo_maxwidth)
00478 levelinfo_maxwidth = len;
00479 }
00480 }
00481
00482 for (i = 0; levelinfo[i].what >= 0; i++) {
00483 int j = ci->levels[levelinfo[i].what];
00484
00485 if (j == ACCESS_INVALID) {
00486 j = levelinfo[i].what;
00487
00488 if (j == CA_AUTOOP || j == CA_AUTODEOP || j == CA_AUTOVOICE
00489 || j == CA_NOJOIN) {
00490 notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_DISABLED,
00491 levelinfo_maxwidth, levelinfo[i].name);
00492 } else {
00493 notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_DISABLED,
00494 levelinfo_maxwidth, levelinfo[i].name);
00495 }
00496 } else if (j == ACCESS_FOUNDER) {
00497 notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_FOUNDER,
00498 levelinfo_maxwidth, levelinfo[i].name);
00499 } else {
00500 notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_NORMAL,
00501 levelinfo_maxwidth, levelinfo[i].name, j);
00502 }
00503 }
00504
00505 } else if (stricmp(cmd, "RESET") == 0) {
00506 reset_levels(ci);
00507
00508 alog("%s: %s!%s@%s reset levels definitions on channel %s",
00509 s_ChanServ, u->nick, u->username, u->host, ci->name);
00510 notice_lang(s_ChanServ, u, CHAN_LEVELS_RESET, chan);
00511 } else {
00512 syntax_error(s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
00513 }
00514 return MOD_CONT;
00515 }