00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "module.h"
00017
00018
00019 int do_access(User * u);
00020 int do_levels(User * u);
00021
00022 void myChanServHelp(User * u);
00023
00030 int AnopeInit(int argc, char **argv)
00031 {
00032 Command *c;
00033
00034 moduleAddAuthor("Anope");
00035 moduleAddVersion
00036 ("$Id: cs_access.c 1423 2008-09-01 01:00:25Z viper $");
00037 moduleSetType(CORE);
00038
00039 c = createCommand("ACCESS", do_access, NULL, CHAN_HELP_ACCESS, -1, -1,
00040 -1, -1);
00041 moduleAddCommand(CHANSERV, c, MOD_UNIQUE);
00042 c = createCommand("ACCESS LEVELS", NULL, NULL, CHAN_HELP_ACCESS_LEVELS,
00043 -1, -1, -1, -1);
00044 moduleAddCommand(CHANSERV, c, MOD_UNIQUE);
00045 c = createCommand("LEVELS", do_levels, NULL, CHAN_HELP_LEVELS, -1, -1,
00046 -1, -1);
00047 moduleAddCommand(CHANSERV, c, MOD_UNIQUE);
00048 moduleSetChanHelp(myChanServHelp);
00049
00050 return MOD_CONT;
00051 }
00052
00056 void AnopeFini(void)
00057 {
00058
00059 }
00060
00061
00062
00067 void myChanServHelp(User * u)
00068 {
00069 notice_lang(s_ChanServ, u, CHAN_HELP_CMD_ACCESS);
00070 notice_lang(s_ChanServ, u, CHAN_HELP_CMD_LEVELS);
00071 }
00072
00073
00074 static int access_del(User * u, ChannelInfo *ci, ChanAccess * access, int *perm, int uacc)
00075 {
00076 char *nick;
00077 if (!access->in_use)
00078 return 0;
00079 if (!is_services_admin(u) && uacc <= access->level) {
00080 (*perm)++;
00081 return 0;
00082 }
00083 nick = access->nc->display;
00084 access->nc = NULL;
00085 access->in_use = 0;
00086 send_event(EVENT_ACCESS_DEL, 3, ci->name, u->nick, nick);
00087 return 1;
00088 }
00089
00090 static int access_del_callback(User * u, int num, va_list args)
00091 {
00092 ChannelInfo *ci = va_arg(args, ChannelInfo *);
00093 int *last = va_arg(args, int *);
00094 int *perm = va_arg(args, int *);
00095 int uacc = va_arg(args, int);
00096 if (num < 1 || num > ci->accesscount)
00097 return 0;
00098 *last = num;
00099 return access_del(u, ci, &ci->access[num - 1], perm, uacc);
00100 }
00101
00102
00103 static int access_list(User * u, int index, ChannelInfo * ci,
00104 int *sent_header)
00105 {
00106 ChanAccess *access = &ci->access[index];
00107 const char *xop;
00108
00109 if (!access->in_use)
00110 return 0;
00111
00112 if (!*sent_header) {
00113 notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_HEADER, ci->name);
00114 *sent_header = 1;
00115 }
00116
00117 if (ci->flags & CI_XOP) {
00118 xop = get_xop_level(access->level);
00119 notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_XOP_FORMAT, index + 1,
00120 xop, access->nc->display);
00121 } else {
00122 notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_AXS_FORMAT, index + 1,
00123 access->level, access->nc->display);
00124 }
00125 return 1;
00126 }
00127
00128 static int access_list_callback(User * u, int num, va_list args)
00129 {
00130 ChannelInfo *ci = va_arg(args, ChannelInfo *);
00131 int *sent_header = va_arg(args, int *);
00132 if (num < 1 || num > ci->accesscount)
00133 return 0;
00134 return access_list(u, num - 1, ci, sent_header);
00135 }
00136
00137
00143 int do_access(User * u)
00144 {
00145 char *chan = strtok(NULL, " ");
00146 char *cmd = strtok(NULL, " ");
00147 char *nick = strtok(NULL, " ");
00148 char *s = strtok(NULL, " ");
00149 char event_access[BUFSIZE];
00150
00151 ChannelInfo *ci;
00152 NickAlias *na = NULL;
00153 NickCore *nc;
00154 ChanAccess *access;
00155
00156 int i;
00157 int level = 0, ulev;
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 || !stricmp(cmd, "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) {
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
00240
00241
00242
00243
00244
00245
00246
00247 if (i < CSAccessMax) {
00248 ci->accesscount++;
00249 ci->access =
00250 srealloc(ci->access,
00251 sizeof(ChanAccess) * ci->accesscount);
00252 } else {
00253 notice_lang(s_ChanServ, u, CHAN_ACCESS_REACHED_LIMIT,
00254 CSAccessMax);
00255 return MOD_CONT;
00256 }
00257
00258 access = &ci->access[i];
00259 access->nc = nc;
00260 access->in_use = 1;
00261 access->level = level;
00262 access->last_seen = 0;
00263
00264 snprintf(event_access, BUFSIZE, "%d", access->level);
00265 send_event(EVENT_ACCESS_ADD, 4, ci->name, u->nick, na->nick,
00266 event_access);
00267 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);
00268 notice_lang(s_ChanServ, u, CHAN_ACCESS_ADDED, nc->display,
00269 ci->name, access->level);
00270 } else if (stricmp(cmd, "DEL") == 0) {
00271 int deleted, a, b;
00272 if (readonly) {
00273 notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
00274 return MOD_CONT;
00275 }
00276
00277 if (ci->accesscount == 0) {
00278 notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_EMPTY, chan);
00279 return MOD_CONT;
00280 }
00281
00282
00283 if (isdigit(*nick) && strspn(nick, "1234567890,-") == strlen(nick)) {
00284 int count, last = -1, perm = 0;
00285 deleted = process_numlist(nick, &count, access_del_callback, u,
00286 ci, &last, &perm, get_access(u, ci));
00287 if (!deleted) {
00288 if (perm) {
00289 notice_lang(s_ChanServ, u, PERMISSION_DENIED);
00290 } else if (count == 1) {
00291 notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_SUCH_ENTRY,
00292 last, ci->name);
00293 } else {
00294 notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_MATCH,
00295 ci->name);
00296 }
00297 } else 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 if (deleted) {
00335
00336 for (b = 0; b < ci->accesscount; b++) {
00337 if (ci->access[b].in_use) {
00338 for (a = 0; a < ci->accesscount; a++) {
00339 if (a > b)
00340 break;
00341 if (!ci->access[a].in_use) {
00342 ci->access[a].in_use = 1;
00343 ci->access[a].level = ci->access[b].level;
00344 ci->access[a].nc = ci->access[b].nc;
00345 ci->access[a].last_seen =
00346 ci->access[b].last_seen;
00347 ci->access[b].nc = NULL;
00348 ci->access[b].in_use = 0;
00349 break;
00350 }
00351 }
00352 }
00353 }
00354
00355
00356
00357 for (i = ci->accesscount - 1; i >= 0; i--) {
00358 if (ci->access[i].in_use == 1)
00359 break;
00360
00361 ci->accesscount--;
00362 }
00363 ci->access =
00364 srealloc(ci->access,sizeof(ChanAccess) * ci->accesscount);
00365
00366
00367
00368 if (na)
00369 send_event(EVENT_ACCESS_DEL, 3, ci->name, u->nick, na->nick);
00370 else
00371 send_event(EVENT_ACCESS_DEL, 2, ci->name, u->nick);
00372 }
00373 } else if (stricmp(cmd, "LIST") == 0) {
00374 int sent_header = 0;
00375
00376 if (ci->accesscount == 0) {
00377 notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_EMPTY, chan);
00378 return MOD_CONT;
00379 }
00380 if (nick && strspn(nick, "1234567890,-") == strlen(nick)) {
00381 process_numlist(nick, NULL, access_list_callback, u, ci,
00382 &sent_header);
00383 } else {
00384 for (i = 0; i < ci->accesscount; i++) {
00385 if (nick && ci->access[i].nc
00386 && !match_wild_nocase(nick, ci->access[i].nc->display))
00387 continue;
00388 access_list(u, i, ci, &sent_header);
00389 }
00390 }
00391 if (!sent_header) {
00392 notice_lang(s_ChanServ, u, CHAN_ACCESS_NO_MATCH, chan);
00393 } else {
00394 notice_lang(s_ChanServ, u, CHAN_ACCESS_LIST_FOOTER, ci->name);
00395 }
00396 } else if (stricmp(cmd, "CLEAR") == 0) {
00397
00398 if (readonly) {
00399 notice_lang(s_ChanServ, u, CHAN_ACCESS_DISABLED);
00400 return MOD_CONT;
00401 }
00402
00403 if (!is_servadmin && !is_founder(u, ci)) {
00404 notice_lang(s_ChanServ, u, PERMISSION_DENIED);
00405 return MOD_CONT;
00406 }
00407
00408 free(ci->access);
00409 ci->access = NULL;
00410 ci->accesscount = 0;
00411
00412 send_event(EVENT_ACCESS_CLEAR, 2, ci->name, u->nick);
00413
00414 notice_lang(s_ChanServ, u, CHAN_ACCESS_CLEAR, ci->name);
00415 alog("%s: %s!%s@%s (level %d) cleared access list on %s",
00416 s_ChanServ, u->nick, u->username, u->host,
00417 get_access(u, ci), chan);
00418
00419 } else {
00420 syntax_error(s_ChanServ, u, "ACCESS", CHAN_ACCESS_SYNTAX);
00421 }
00422 return MOD_CONT;
00423 }
00424
00425
00426 int do_levels(User * u)
00427 {
00428 char *chan = strtok(NULL, " ");
00429 char *cmd = strtok(NULL, " ");
00430 char *what = strtok(NULL, " ");
00431 char *s = strtok(NULL, " ");
00432 char *error;
00433
00434 ChannelInfo *ci;
00435 int level;
00436 int i;
00437
00438
00439
00440
00441 if (!cmd
00442 || ((stricmp(cmd, "SET") == 0) ? !s
00443 : ((strnicmp(cmd, "DIS", 3) == 0)) ? (!what || s) : !!what)) {
00444 syntax_error(s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
00445 } else if (!(ci = cs_findchan(chan))) {
00446 notice_lang(s_ChanServ, u, CHAN_X_NOT_REGISTERED, chan);
00447 } else if (ci->flags & CI_VERBOTEN) {
00448 notice_lang(s_ChanServ, u, CHAN_X_FORBIDDEN, chan);
00449 } else if (ci->flags & CI_XOP) {
00450 notice_lang(s_ChanServ, u, CHAN_LEVELS_XOP);
00451 } else if (!is_founder(u, ci) && !is_services_admin(u)) {
00452 notice_lang(s_ChanServ, u, ACCESS_DENIED);
00453 } else if (stricmp(cmd, "SET") == 0) {
00454 level = strtol(s, &error, 10);
00455
00456 if (*error != '\0') {
00457 syntax_error(s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
00458 return MOD_CONT;
00459 }
00460
00461 if (level <= ACCESS_INVALID || level >= ACCESS_FOUNDER) {
00462 notice_lang(s_ChanServ, u, CHAN_LEVELS_RANGE,
00463 ACCESS_INVALID + 1, ACCESS_FOUNDER - 1);
00464 return MOD_CONT;
00465 }
00466
00467 for (i = 0; levelinfo[i].what >= 0; i++) {
00468 if (stricmp(levelinfo[i].name, what) == 0) {
00469 ci->levels[levelinfo[i].what] = level;
00470
00471 alog("%s: %s!%s@%s set level %s on channel %s to %d",
00472 s_ChanServ, u->nick, u->username, u->host,
00473 levelinfo[i].name, ci->name, level);
00474 notice_lang(s_ChanServ, u, CHAN_LEVELS_CHANGED,
00475 levelinfo[i].name, chan, level);
00476 return MOD_CONT;
00477 }
00478 }
00479
00480 notice_lang(s_ChanServ, u, CHAN_LEVELS_UNKNOWN, what, s_ChanServ);
00481
00482 } else if (stricmp(cmd, "DIS") == 0 || stricmp(cmd, "DISABLE") == 0) {
00483 for (i = 0; levelinfo[i].what >= 0; i++) {
00484 if (stricmp(levelinfo[i].name, what) == 0) {
00485 ci->levels[levelinfo[i].what] = ACCESS_INVALID;
00486
00487 alog("%s: %s!%s@%s disabled level %s on channel %s",
00488 s_ChanServ, u->nick, u->username, u->host,
00489 levelinfo[i].name, ci->name);
00490 notice_lang(s_ChanServ, u, CHAN_LEVELS_DISABLED,
00491 levelinfo[i].name, chan);
00492 return MOD_CONT;
00493 }
00494 }
00495
00496 notice_lang(s_ChanServ, u, CHAN_LEVELS_UNKNOWN, what, s_ChanServ);
00497 } else if (stricmp(cmd, "LIST") == 0) {
00498 int i;
00499
00500 notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_HEADER, chan);
00501
00502 if (!levelinfo_maxwidth) {
00503 for (i = 0; levelinfo[i].what >= 0; i++) {
00504 int len = strlen(levelinfo[i].name);
00505 if (len > levelinfo_maxwidth)
00506 levelinfo_maxwidth = len;
00507 }
00508 }
00509
00510 for (i = 0; levelinfo[i].what >= 0; i++) {
00511 int j = ci->levels[levelinfo[i].what];
00512
00513 if (j == ACCESS_INVALID) {
00514 j = levelinfo[i].what;
00515
00516 if (j == CA_AUTOOP || j == CA_AUTODEOP || j == CA_AUTOVOICE
00517 || j == CA_NOJOIN) {
00518 notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_DISABLED,
00519 levelinfo_maxwidth, levelinfo[i].name);
00520 } else {
00521 notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_DISABLED,
00522 levelinfo_maxwidth, levelinfo[i].name);
00523 }
00524 } else if (j == ACCESS_FOUNDER) {
00525 notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_FOUNDER,
00526 levelinfo_maxwidth, levelinfo[i].name);
00527 } else {
00528 notice_lang(s_ChanServ, u, CHAN_LEVELS_LIST_NORMAL,
00529 levelinfo_maxwidth, levelinfo[i].name, j);
00530 }
00531 }
00532
00533 } else if (stricmp(cmd, "RESET") == 0) {
00534 reset_levels(ci);
00535
00536 alog("%s: %s!%s@%s reset levels definitions on channel %s",
00537 s_ChanServ, u->nick, u->username, u->host, ci->name);
00538 notice_lang(s_ChanServ, u, CHAN_LEVELS_RESET, chan);
00539 } else {
00540 syntax_error(s_ChanServ, u, "LEVELS", CHAN_LEVELS_SYNTAX);
00541 }
00542 return MOD_CONT;
00543 }