cs_access.c

Go to the documentation of this file.
00001 /* ChanServ core functions
00002  *
00003  * (C) 2003-2008 Anope Team
00004  * Contact us at info@anope.org
00005  *
00006  * Please read COPYING and README for further details.
00007  *
00008  * Based on the original code of Epona by Lara.
00009  * Based on the original code of Services by Andy Church. 
00010  * 
00011  * $Id: cs_access.c 1423 2008-09-01 01:00:25Z viper $
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     /* If LIST, we don't *require* any parameters, but we can take any.
00162      * If DEL, we require a nick and no level.
00163      * Else (ADD), we require a level (which implies a nick). */
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         /* We still allow LIST in xOP mode, but not others */
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                 /* Don't allow lowering from a level >= ulev */
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         /* All entries should be in use so we no longer need
00240          * to go over the entire list..
00241         for (i = 0; i < ci->accesscount; i++) {
00242             if (!ci->access[i].in_use)
00243                 break;
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         /* Special case: is it a number/list?  Only do search if it isn't. */
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             /* Reordering - DrStein */
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             /* After reordering only the entries at the end could still be empty.
00356              * We ll free the places no longer in use... */
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             /* We don't know the nick if someone used numbers, so we trigger the event without
00367              * nick param. We just do this once, even if someone enters a range. -Certus */
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     /* If SET, we want two extra parameters; if DIS[ABLE] or FOUNDER, we want only
00439      * one; else, we want none.
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 }

Generated on Sun Oct 5 09:06:54 2008 for Anope by  doxygen 1.5.7.1