sessions.c

Go to the documentation of this file.
00001 /* Session Limiting functions.
00002  *
00003  * (C) 2003-2007 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: sessions.c 1295 2007-08-31 20:40:36Z drstein $ 
00012  *
00013  */
00014 
00015 #include "services.h"
00016 #include "pseudo.h"
00017 
00018 /*************************************************************************/
00019 
00020 /* SESSION LIMITING
00021  *
00022  * The basic idea of session limiting is to prevent one host from having more 
00023  * than a specified number of sessions (client connections/clones) on the 
00024  * network at any one time. To do this we have a list of sessions and 
00025  * exceptions. Each session structure records information about a single host,
00026  * including how many clients (sessions) that host has on the network. When a 
00027  * host reaches it's session limit, no more clients from that host will be 
00028  * allowed to connect.
00029  *
00030  * When a client connects to the network, we check to see if their host has 
00031  * reached the default session limit per host, and thus whether it is allowed 
00032  * any more. If it has reached the limit, we kill the connecting client; all 
00033  * the other clients are left alone. Otherwise we simply increment the counter 
00034  * within the session structure. When a client disconnects, we decrement the 
00035  * counter. When the counter reaches 0, we free the session.
00036  *
00037  * Exceptions allow one to specify custom session limits for a specific host 
00038  * or a range thereof. The first exception that the host matches is the one 
00039  * used.
00040  *
00041  * "Session Limiting" is likely to slow down services when there are frequent 
00042  * client connects and disconnects. The size of the exception list can also 
00043  * play a large role in this performance decrease. It is therefore recommened 
00044  * that you keep the number of exceptions to a minimum. A very simple hashing 
00045  * method is currently used to store the list of sessions. I'm sure there is 
00046  * room for improvement and optimisation of this, along with the storage of 
00047  * exceptions. Comments and suggestions are more than welcome!
00048  *
00049  * -TheShadow (02 April 1999)
00050  */
00051 
00052 /*************************************************************************/
00053 
00054 /* I'm sure there is a better way to hash the list of hosts for which we are
00055  * storing session information. This should be sufficient for the mean time.
00056  * -TheShadow */
00057 
00058 #define HASH(host)      (((host)[0]&31)<<5 | ((host)[1]&31))
00059 
00060 Session *sessionlist[1024];
00061 int32 nsessions = 0;
00062 
00063 Exception *exceptions = NULL;
00064 int16 nexceptions = 0;
00065 
00066 /*************************************************************************/
00067 /****************************** Statistics *******************************/
00068 /*************************************************************************/
00069 
00070 void get_session_stats(long *nrec, long *memuse)
00071 {
00072     Session *session;
00073     long mem;
00074     int i;
00075 
00076     mem = sizeof(Session) * nsessions;
00077     for (i = 0; i < 1024; i++) {
00078         for (session = sessionlist[i]; session; session = session->next) {
00079             mem += strlen(session->host) + 1;
00080         }
00081     }
00082 
00083     *nrec = nsessions;
00084     *memuse = mem;
00085 }
00086 
00087 void get_exception_stats(long *nrec, long *memuse)
00088 {
00089     long mem;
00090     int i;
00091 
00092     mem = sizeof(Exception) * nexceptions;
00093     for (i = 0; i < nexceptions; i++) {
00094         mem += strlen(exceptions[i].mask) + 1;
00095         mem += strlen(exceptions[i].reason) + 1;
00096     }
00097     *nrec = nexceptions;
00098     *memuse = mem;
00099 }
00100 
00101 /*************************************************************************/
00102 /************************* Session List Display **************************/
00103 /*************************************************************************/
00104 
00105 /* Syntax: SESSION LIST threshold
00106  *  Lists all sessions with atleast threshold clients.
00107  *  The threshold value must be greater than 1. This is to prevent 
00108  *  accidental listing of the large number of single client sessions.
00109  *
00110  * Syntax: SESSION VIEW host
00111  *  Displays detailed session information about the supplied host.
00112  */
00113 
00114 int do_session(User * u)
00115 {
00116     Session *session;
00117     Exception *exception;
00118     char *cmd = strtok(NULL, " ");
00119     char *param1 = strtok(NULL, " ");
00120     int mincount;
00121     int i;
00122 
00123     if (!LimitSessions) {
00124         notice_lang(s_OperServ, u, OPER_SESSION_DISABLED);
00125         return MOD_CONT;
00126     }
00127 
00128     if (!cmd)
00129         cmd = "";
00130 
00131     if (stricmp(cmd, "LIST") == 0) {
00132         if (!param1) {
00133             syntax_error(s_OperServ, u, "SESSION",
00134                          OPER_SESSION_LIST_SYNTAX);
00135 
00136         } else if ((mincount = atoi(param1)) <= 1) {
00137             notice_lang(s_OperServ, u, OPER_SESSION_INVALID_THRESHOLD);
00138 
00139         } else {
00140             notice_lang(s_OperServ, u, OPER_SESSION_LIST_HEADER, mincount);
00141             notice_lang(s_OperServ, u, OPER_SESSION_LIST_COLHEAD);
00142             for (i = 0; i < 1024; i++) {
00143                 for (session = sessionlist[i]; session;
00144                      session = session->next) {
00145                     if (session->count >= mincount)
00146                         notice_lang(s_OperServ, u,
00147                                     OPER_SESSION_LIST_FORMAT,
00148                                     session->count, session->host);
00149                 }
00150             }
00151         }
00152     } else if (stricmp(cmd, "VIEW") == 0) {
00153         if (!param1) {
00154             syntax_error(s_OperServ, u, "SESSION",
00155                          OPER_SESSION_VIEW_SYNTAX);
00156 
00157         } else {
00158             session = findsession(param1);
00159             if (!session) {
00160                 notice_lang(s_OperServ, u, OPER_SESSION_NOT_FOUND, param1);
00161             } else {
00162                 exception = find_host_exception(param1);
00163 
00164                 notice_lang(s_OperServ, u, OPER_SESSION_VIEW_FORMAT,
00165                             param1, session->count,
00166                             exception ? exception->
00167                             limit : DefSessionLimit);
00168             }
00169         }
00170 
00171     } else {
00172         syntax_error(s_OperServ, u, "SESSION", OPER_SESSION_SYNTAX);
00173     }
00174     return MOD_CONT;
00175 }
00176 
00177 /*************************************************************************/
00178 /********************* Internal Session Functions ************************/
00179 /*************************************************************************/
00180 
00181 Session *findsession(const char *host)
00182 {
00183     Session *session;
00184     int i;
00185 
00186     if (!host) {
00187         return NULL;
00188     }
00189 
00190     for (i = 0; i < 1024; i++) {
00191         for (session = sessionlist[i]; session; session = session->next) {
00192             if (stricmp(host, session->host) == 0) {
00193                 return session;
00194             }
00195         }
00196     }
00197 
00198     return NULL;
00199 }
00200 
00201 /* Attempt to add a host to the session list. If the addition of the new host
00202  * causes the the session limit to be exceeded, kill the connecting user.
00203  * Returns 1 if the host was added or 0 if the user was killed.
00204  */
00205 
00206 int add_session(char *nick, char *host, char *hostip)
00207 {
00208     Session *session, **list;
00209     Exception *exception;
00210     int sessionlimit = 0;
00211 
00212     session = findsession(host);
00213 
00214     if (session) {
00215         exception = find_hostip_exception(host, hostip);
00216 
00217         if (checkDefCon(DEFCON_REDUCE_SESSION)) {
00218             sessionlimit =
00219                 exception ? exception->limit : DefConSessionLimit;
00220         } else {
00221             sessionlimit = exception ? exception->limit : DefSessionLimit;
00222         }
00223 
00224         if (sessionlimit != 0 && session->count >= sessionlimit) {
00225             if (SessionLimitExceeded)
00226                 notice(s_OperServ, nick, SessionLimitExceeded, host);
00227             if (SessionLimitDetailsLoc)
00228                 notice(s_OperServ, nick, "%s", SessionLimitDetailsLoc);
00229 
00230             /* We don't use kill_user() because a user stucture has not yet
00231              * been created. Simply kill the user. -TheShadow
00232              */
00233             kill_user(s_OperServ, nick, "Session limit exceeded");
00234 
00235             session->hits++;
00236             if (MaxSessionKill && session->hits >= MaxSessionKill) {
00237                 char akillmask[BUFSIZE];
00238                 snprintf(akillmask, sizeof(akillmask), "*@%s", host);
00239                 add_akill(NULL, akillmask, s_OperServ,
00240                           time(NULL) + SessionAutoKillExpiry,
00241                           "Session limit exceeded");
00242                 anope_cmd_global(s_OperServ,
00243                                  "Added a temporary AKILL for \2%s\2 due to excessive connections",
00244                                  akillmask);
00245             }
00246             return 0;
00247         } else {
00248             session->count++;
00249             return 1;
00250         }
00251     }
00252 
00253     nsessions++;
00254     session = scalloc(sizeof(Session), 1);
00255     session->host = sstrdup(host);
00256     list = &sessionlist[HASH(session->host)];
00257     session->next = *list;
00258     if (*list)
00259         (*list)->prev = session;
00260     *list = session;
00261     session->count = 1;
00262 
00263     return 1;
00264 }
00265 
00266 void del_session(const char *host)
00267 {
00268     Session *session;
00269 
00270     if (!LimitSessions) {
00271         if (debug) {
00272             alog("debug: del_session called when LimitSessions is disabled");
00273         }
00274         return;
00275     }
00276 
00277     if (!host || !*host) {
00278         if (debug) {
00279             alog("debug: del_session called with NULL values");
00280         }
00281         return;
00282     }
00283 
00284     if (debug >= 2)
00285         alog("debug: del_session() called");
00286 
00287     session = findsession(host);
00288 
00289     if (!session) {
00290         if (debug) {
00291             anope_cmd_global(s_OperServ,
00292                              "WARNING: Tried to delete non-existant session: \2%s",
00293                              host);
00294             alog("session: Tried to delete non-existant session: %s",
00295                  host);
00296         }
00297         return;
00298     }
00299 
00300     if (session->count > 1) {
00301         session->count--;
00302         return;
00303     }
00304 
00305     if (session->prev)
00306         session->prev->next = session->next;
00307     else
00308         sessionlist[HASH(session->host)] = session->next;
00309     if (session->next)
00310         session->next->prev = session->prev;
00311 
00312     if (debug >= 2)
00313         alog("debug: del_session(): free session structure");
00314 
00315     free(session->host);
00316     free(session);
00317 
00318     nsessions--;
00319 
00320     if (debug >= 2)
00321         alog("debug: del_session() done");
00322 }
00323 
00324 
00325 /*************************************************************************/
00326 /********************** Internal Exception Functions *********************/
00327 /*************************************************************************/
00328 
00329 void expire_exceptions(void)
00330 {
00331     int i;
00332     time_t now = time(NULL);
00333 
00334     for (i = 0; i < nexceptions; i++) {
00335         if (exceptions[i].expires == 0 || exceptions[i].expires > now)
00336             continue;
00337         if (WallExceptionExpire)
00338             anope_cmd_global(s_OperServ,
00339                              "Session limit exception for %s has expired.",
00340                              exceptions[i].mask);
00341         free(exceptions[i].mask);
00342         free(exceptions[i].reason);
00343         nexceptions--;
00344         memmove(exceptions + i, exceptions + i + 1,
00345                 sizeof(Exception) * (nexceptions - i));
00346         exceptions = srealloc(exceptions, sizeof(Exception) * nexceptions);
00347         i--;
00348     }
00349 }
00350 
00351 /* Find the first exception this host matches and return it. */
00352 Exception *find_host_exception(const char *host)
00353 {
00354     int i;
00355 
00356     for (i = 0; i < nexceptions; i++) {
00357         if ((match_wild_nocase(exceptions[i].mask, host))) {
00358             return &exceptions[i];
00359         }
00360     }
00361 
00362     return NULL;
00363 }
00364 
00365 /* Same as find_host_exception() except
00366  * this tries to find the exception by IP also. */
00367 Exception *find_hostip_exception(const char *host, const char *hostip)
00368 {
00369     int i;
00370 
00371     for (i = 0; i < nexceptions; i++) {
00372         if ((match_wild_nocase(exceptions[i].mask, host))
00373             || ((ircd->nickip && hostip)
00374                 && (match_wild_nocase(exceptions[i].mask, hostip)))) {
00375             return &exceptions[i];
00376         }
00377     }
00378 
00379     return NULL;
00380 }
00381 
00382 /*************************************************************************/
00383 /*********************** Exception Load/Save *****************************/
00384 /*************************************************************************/
00385 
00386 #define SAFE(x) do {                                    \
00387     if ((x) < 0) {                                      \
00388         if (!forceload)                                 \
00389             fatal("Read error on %s", ExceptionDBName); \
00390         nexceptions = i;                                \
00391         break;                                          \
00392     }                                                   \
00393 } while (0)
00394 
00395 void load_exceptions()
00396 {
00397     dbFILE *f;
00398     int i;
00399     uint16 n;
00400     uint16 tmp16;
00401     uint32 tmp32;
00402 
00403     if (!
00404         (f = open_db(s_OperServ, ExceptionDBName, "r", EXCEPTION_VERSION)))
00405         return;
00406     switch (i = get_file_version(f)) {
00407     case 9:
00408     case 8:
00409     case 7:
00410         SAFE(read_int16(&n, f));
00411         nexceptions = n;
00412         exceptions = scalloc(sizeof(Exception) * nexceptions, 1);
00413         if (!nexceptions) {
00414             close_db(f);
00415             return;
00416         }
00417         for (i = 0; i < nexceptions; i++) {
00418             SAFE(read_string(&exceptions[i].mask, f));
00419             SAFE(read_int16(&tmp16, f));
00420             exceptions[i].limit = tmp16;
00421             SAFE(read_buffer(exceptions[i].who, f));
00422             SAFE(read_string(&exceptions[i].reason, f));
00423             SAFE(read_int32(&tmp32, f));
00424             exceptions[i].time = tmp32;
00425             SAFE(read_int32(&tmp32, f));
00426             exceptions[i].expires = tmp32;
00427         }
00428         break;
00429 
00430     default:
00431         fatal("Unsupported version (%d) on %s", i, ExceptionDBName);
00432     }                           /* switch (ver) */
00433 
00434     close_db(f);
00435 }
00436 
00437 #undef SAFE
00438 
00439 /*************************************************************************/
00440 
00441 #define SAFE(x) do {                                            \
00442     if ((x) < 0) {                                              \
00443         restore_db(f);                                          \
00444         log_perror("Write error on %s", ExceptionDBName);       \
00445         if (time(NULL) - lastwarn > WarningTimeout) {           \
00446             anope_cmd_global(NULL, "Write error on %s: %s", ExceptionDBName,  \
00447                         strerror(errno));                       \
00448             lastwarn = time(NULL);                              \
00449         }                                                       \
00450         return;                                                 \
00451     }                                                           \
00452 } while (0)
00453 
00454 void save_exceptions()
00455 {
00456     dbFILE *f;
00457     int i;
00458     static time_t lastwarn = 0;
00459 
00460     if (!
00461         (f = open_db(s_OperServ, ExceptionDBName, "w", EXCEPTION_VERSION)))
00462         return;
00463     SAFE(write_int16(nexceptions, f));
00464     for (i = 0; i < nexceptions; i++) {
00465         SAFE(write_string(exceptions[i].mask, f));
00466         SAFE(write_int16(exceptions[i].limit, f));
00467         SAFE(write_buffer(exceptions[i].who, f));
00468         SAFE(write_string(exceptions[i].reason, f));
00469         SAFE(write_int32(exceptions[i].time, f));
00470         SAFE(write_int32(exceptions[i].expires, f));
00471     }
00472     close_db(f);
00473 }
00474 
00475 #undef SAFE
00476 
00477 /*************************************************************************/
00478 
00479 void save_rdb_exceptions()
00480 {
00481 #ifdef USE_RDB
00482     int i;
00483     Exception *e;
00484 
00485     if (!rdb_open())
00486         return;
00487     if (rdb_tag_table("anope_os_exceptions") == 0) {
00488         alog("Unable to tag table 'anope_os_exceptions' - Exception RDB save failed.");
00489         return;
00490     }
00491     for (i = 0; i < nexceptions; i++) {
00492         e = &exceptions[i];
00493         if (rdb_save_exceptions(e) == 0) {
00494             alog("Unable to save Exception '%s' - Exception RDB save failed.", e->mask);
00495             return;
00496         }
00497     }
00498     if (rdb_clean_table("anope_os_exceptions") == 0) {
00499         alog("Unable to clean table 'anope_os_exceptions' - Exception RDB save failed.");
00500         return;
00501     }
00502     rdb_close();
00503 #endif
00504 }
00505 
00506 /*************************************************************************/
00507 /************************ Exception Manipulation *************************/
00508 /*************************************************************************/
00509 
00510 int exception_add(User * u, const char *mask, const int limit,
00511                   const char *reason, const char *who,
00512                   const time_t expires)
00513 {
00514     int i;
00515 
00516     /* Check if an exception already exists for this mask */
00517     for (i = 0; i < nexceptions; i++) {
00518         if (!stricmp(mask, exceptions[i].mask)) {
00519             if (exceptions[i].limit != limit) {
00520                 exceptions[i].limit = limit;
00521                 if (u)
00522                     notice_lang(s_OperServ, u, OPER_EXCEPTION_CHANGED,
00523                                 mask, exceptions[i].limit);
00524                 return -2;
00525             } else {
00526                 if (u)
00527                     notice_lang(s_OperServ, u, OPER_EXCEPTION_EXISTS,
00528                                 mask);
00529                 return -1;
00530             }
00531         }
00532     }
00533 
00534     nexceptions++;
00535     exceptions = srealloc(exceptions, sizeof(Exception) * nexceptions);
00536 
00537     exceptions[nexceptions - 1].mask = sstrdup(mask);
00538     exceptions[nexceptions - 1].limit = limit;
00539     exceptions[nexceptions - 1].reason = sstrdup(reason);
00540     exceptions[nexceptions - 1].time = time(NULL);
00541     strscpy(exceptions[nexceptions - 1].who, who, NICKMAX);
00542     exceptions[nexceptions - 1].expires = expires;
00543     exceptions[nexceptions - 1].num = nexceptions - 1;
00544 
00545     return 1;
00546 }
00547 
00548 /*************************************************************************/
00549 
00550 static int exception_del(const int index)
00551 {
00552     if (index < 0 || index >= nexceptions)
00553         return 0;
00554 
00555     free(exceptions[index].mask);
00556     free(exceptions[index].reason);
00557     nexceptions--;
00558     memmove(exceptions + index, exceptions + index + 1,
00559             sizeof(Exception) * (nexceptions - index));
00560     exceptions = srealloc(exceptions, sizeof(Exception) * nexceptions);
00561 
00562     return 1;
00563 }
00564 
00565 /* We use the "num" property to keep track of the position of each exception
00566  * when deleting using ranges. This is because an exception's position changes
00567  * as others are deleted. The positions will be recalculated once the process
00568  * is complete. -TheShadow
00569  */
00570 
00571 static int exception_del_callback(User * u, int num, va_list args)
00572 {
00573     int i;
00574     int *last = va_arg(args, int *);
00575 
00576     *last = num;
00577     for (i = 0; i < nexceptions; i++) {
00578         if (num - 1 == exceptions[i].num)
00579             break;
00580     }
00581     if (i < nexceptions)
00582         return exception_del(i);
00583     else
00584         return 0;
00585 }
00586 
00587 static int exception_list(User * u, const int index, int *sent_header)
00588 {
00589     if (index < 0 || index >= nexceptions)
00590         return 0;
00591     if (!*sent_header) {
00592         notice_lang(s_OperServ, u, OPER_EXCEPTION_LIST_HEADER);
00593         notice_lang(s_OperServ, u, OPER_EXCEPTION_LIST_COLHEAD);
00594         *sent_header = 1;
00595     }
00596     notice_lang(s_OperServ, u, OPER_EXCEPTION_LIST_FORMAT, index + 1,
00597                 exceptions[index].limit, exceptions[index].mask);
00598     return 1;
00599 }
00600 
00601 static int exception_list_callback(User * u, int num, va_list args)
00602 {
00603     int *sent_header = va_arg(args, int *);
00604 
00605     return exception_list(u, num - 1, sent_header);
00606 }
00607 
00608 static int exception_view(User * u, const int index, int *sent_header)
00609 {
00610     char timebuf[32], expirebuf[256];
00611     struct tm tm;
00612     time_t t = time(NULL);
00613 
00614     if (index < 0 || index >= nexceptions)
00615         return 0;
00616     if (!*sent_header) {
00617         notice_lang(s_OperServ, u, OPER_EXCEPTION_LIST_HEADER);
00618         *sent_header = 1;
00619     }
00620 
00621     tm = *localtime(exceptions[index].time ? &exceptions[index].time : &t);
00622     strftime_lang(timebuf, sizeof(timebuf),
00623                   u, STRFTIME_SHORT_DATE_FORMAT, &tm);
00624 
00625     expire_left(u->na, expirebuf, sizeof(expirebuf),
00626                 exceptions[index].expires);
00627 
00628     notice_lang(s_OperServ, u, OPER_EXCEPTION_VIEW_FORMAT,
00629                 index + 1, exceptions[index].mask,
00630                 *exceptions[index].who ?
00631                 exceptions[index].who : "<unknown>",
00632                 timebuf, expirebuf, exceptions[index].limit,
00633                 exceptions[index].reason);
00634     return 1;
00635 }
00636 
00637 static int exception_view_callback(User * u, int num, va_list args)
00638 {
00639     int *sent_header = va_arg(args, int *);
00640 
00641     return exception_view(u, num - 1, sent_header);
00642 }
00643 
00644 /*************************************************************************/
00645 
00646 /* Syntax: EXCEPTION ADD [+expiry] mask limit reason
00647  *  Adds mask to the exception list with limit as the maximum session
00648  *  limit and +expiry as an optional expiry time.
00649  *
00650  * Syntax: EXCEPTION DEL mask
00651  *  Deletes the first exception that matches mask exactly.
00652  *
00653  * Syntax: EXCEPTION LIST [mask]
00654  *  Lists all exceptions or those matching mask.
00655  *
00656  * Syntax: EXCEPTION VIEW [mask]
00657  *  Displays detailed information about each exception or those matching
00658  *  mask.
00659  *
00660  * Syntax: EXCEPTION MOVE num position
00661  *  Moves the exception at position num to position.
00662  */
00663 
00664 int do_exception(User * u)
00665 {
00666     char *cmd = strtok(NULL, " ");
00667     char *mask, *reason, *expiry, *limitstr;
00668     int limit, expires;
00669     int i;
00670     int x;
00671 
00672     if (!LimitSessions) {
00673         notice_lang(s_OperServ, u, OPER_EXCEPTION_DISABLED);
00674         return MOD_CONT;
00675     }
00676 
00677     if (!cmd)
00678         cmd = "";
00679 
00680     if (stricmp(cmd, "ADD") == 0) {
00681         if (nexceptions >= 32767) {
00682             notice_lang(s_OperServ, u, OPER_EXCEPTION_TOO_MANY);
00683             return MOD_CONT;
00684         }
00685 
00686         mask = strtok(NULL, " ");
00687         if (mask && *mask == '+') {
00688             expiry = mask;
00689             mask = strtok(NULL, " ");
00690         } else {
00691             expiry = NULL;
00692         }
00693         limitstr = strtok(NULL, " ");
00694         reason = strtok(NULL, "");
00695 
00696         if (!reason) {
00697             syntax_error(s_OperServ, u, "EXCEPTION",
00698                          OPER_EXCEPTION_ADD_SYNTAX);
00699             return MOD_CONT;
00700         }
00701 
00702         expires = expiry ? dotime(expiry) : ExceptionExpiry;
00703         if (expires < 0) {
00704             notice_lang(s_OperServ, u, BAD_EXPIRY_TIME);
00705             return MOD_CONT;
00706         } else if (expires > 0) {
00707             expires += time(NULL);
00708         }
00709 
00710         limit = (limitstr && isdigit(*limitstr)) ? atoi(limitstr) : -1;
00711 
00712         if (limit < 0 || limit > MaxSessionLimit) {
00713             notice_lang(s_OperServ, u, OPER_EXCEPTION_INVALID_LIMIT,
00714                         MaxSessionLimit);
00715             return MOD_CONT;
00716 
00717         } else {
00718             if (strchr(mask, '!') || strchr(mask, '@')) {
00719                 notice_lang(s_OperServ, u,
00720                             OPER_EXCEPTION_INVALID_HOSTMASK);
00721                 return MOD_CONT;
00722             }
00723 
00724             x = exception_add(u, mask, limit, reason, u->nick, expires);
00725 
00726             if (x == 1) {
00727                 notice_lang(s_OperServ, u, OPER_EXCEPTION_ADDED, mask,
00728                             limit);
00729             }
00730 
00731             if (readonly)
00732                 notice_lang(s_OperServ, u, READ_ONLY_MODE);
00733         }
00734     } else if (stricmp(cmd, "DEL") == 0) {
00735         mask = strtok(NULL, " ");
00736 
00737         if (!mask) {
00738             syntax_error(s_OperServ, u, "EXCEPTION",
00739                          OPER_EXCEPTION_DEL_SYNTAX);
00740             return MOD_CONT;
00741         }
00742 
00743         if (isdigit(*mask) && strspn(mask, "1234567890,-") == strlen(mask)) {
00744             int count, deleted, last = -1;
00745             deleted =
00746                 process_numlist(mask, &count, exception_del_callback, u,
00747                                 &last);
00748             if (!deleted) {
00749                 if (count == 1) {
00750                     notice_lang(s_OperServ, u,
00751                                 OPER_EXCEPTION_NO_SUCH_ENTRY, last);
00752                 } else {
00753                     notice_lang(s_OperServ, u, OPER_EXCEPTION_NO_MATCH);
00754                 }
00755             } else if (deleted == 1) {
00756                 notice_lang(s_OperServ, u, OPER_EXCEPTION_DELETED_ONE);
00757             } else {
00758                 notice_lang(s_OperServ, u, OPER_EXCEPTION_DELETED_SEVERAL,
00759                             deleted);
00760             }
00761         } else {
00762             int deleted = 0;
00763 
00764             for (i = 0; i < nexceptions; i++) {
00765                 if (stricmp(mask, exceptions[i].mask) == 0) {
00766                     exception_del(i);
00767                     notice_lang(s_OperServ, u, OPER_EXCEPTION_DELETED,
00768                                 mask);
00769                     deleted = 1;
00770                     break;
00771                 }
00772             }
00773             if (!deleted && i == nexceptions)
00774                 notice_lang(s_OperServ, u, OPER_EXCEPTION_NOT_FOUND, mask);
00775         }
00776 
00777         /* Renumber the exception list. I don't believe in having holes in 
00778          * lists - it makes code more complex, harder to debug and we end up 
00779          * with huge index numbers. Imho, fixed numbering is only beneficial
00780          * when one doesn't have range capable manipulation. -TheShadow */
00781 
00782         for (i = 0; i < nexceptions; i++)
00783             exceptions[i].num = i;
00784 
00785         if (readonly)
00786             notice_lang(s_OperServ, u, READ_ONLY_MODE);
00787 
00788     } else if (stricmp(cmd, "MOVE") == 0) {
00789         Exception *exception;
00790         char *n1str = strtok(NULL, " ");        /* From position */
00791         char *n2str = strtok(NULL, " ");        /* To position */
00792         int n1, n2;
00793 
00794         if (!n2str) {
00795             syntax_error(s_OperServ, u, "EXCEPTION",
00796                          OPER_EXCEPTION_MOVE_SYNTAX);
00797             return MOD_CONT;
00798         }
00799 
00800         n1 = atoi(n1str) - 1;
00801         n2 = atoi(n2str) - 1;
00802 
00803         if ((n1 >= 0 && n1 < nexceptions) && (n2 >= 0 && n2 < nexceptions)
00804             && (n1 != n2)) {
00805             exception = scalloc(sizeof(Exception), 1);
00806             memcpy(exception, &exceptions[n1], sizeof(Exception));
00807 
00808             if (n1 < n2) {
00809                 /* Shift upwards */
00810                 memmove(&exceptions[n1], &exceptions[n1 + 1],
00811                         sizeof(Exception) * (n2 - n1));
00812                 memmove(&exceptions[n2], exception, sizeof(Exception));
00813             } else {
00814                 /* Shift downwards */
00815                 memmove(&exceptions[n2 + 1], &exceptions[n2],
00816                         sizeof(Exception) * (n1 - n2));
00817                 memmove(&exceptions[n2], exception, sizeof(Exception));
00818             }
00819 
00820             free(exception);
00821 
00822             notice_lang(s_OperServ, u, OPER_EXCEPTION_MOVED,
00823                         exceptions[n1].mask, n1 + 1, n2 + 1);
00824 
00825             /* Renumber the exception list. See the DEL block above for why. */
00826             for (i = 0; i < nexceptions; i++)
00827                 exceptions[i].num = i;
00828 
00829             if (readonly)
00830                 notice_lang(s_OperServ, u, READ_ONLY_MODE);
00831         } else {
00832             syntax_error(s_OperServ, u, "EXCEPTION",
00833                          OPER_EXCEPTION_MOVE_SYNTAX);
00834         }
00835     } else if (stricmp(cmd, "LIST") == 0) {
00836         int sent_header = 0;
00837         expire_exceptions();
00838         mask = strtok(NULL, " ");
00839         if (mask && strspn(mask, "1234567890,-") == strlen(mask)) {
00840             process_numlist(mask, NULL, exception_list_callback, u,
00841                             &sent_header);
00842         } else {
00843             for (i = 0; i < nexceptions; i++) {
00844                 if (!mask || match_wild_nocase(mask, exceptions[i].mask))
00845                     exception_list(u, i, &sent_header);
00846             }
00847         }
00848         if (!sent_header)
00849             notice_lang(s_OperServ, u, OPER_EXCEPTION_NO_MATCH);
00850 
00851     } else if (stricmp(cmd, "VIEW") == 0) {
00852         int sent_header = 0;
00853         expire_exceptions();
00854         mask = strtok(NULL, " ");
00855         if (mask && strspn(mask, "1234567890,-") == strlen(mask)) {
00856             process_numlist(mask, NULL, exception_view_callback, u,
00857                             &sent_header);
00858         } else {
00859             for (i = 0; i < nexceptions; i++) {
00860                 if (!mask || match_wild_nocase(mask, exceptions[i].mask))
00861                     exception_view(u, i, &sent_header);
00862             }
00863         }
00864         if (!sent_header)
00865             notice_lang(s_OperServ, u, OPER_EXCEPTION_NO_MATCH);
00866 
00867     } else {
00868         syntax_error(s_OperServ, u, "EXCEPTION", OPER_EXCEPTION_SYNTAX);
00869     }
00870     return MOD_CONT;
00871 }
00872 
00873 /*************************************************************************/

Generated on Sun Dec 30 09:26:53 2007 for Anope by  doxygen 1.5.1-20070107