Ignore:
Timestamp:
05/31/07 15:15:23 (5 years ago)
Author:
wd
Message:

Many more changes:

  • Moved the 'core' addon code into various places in the main ircd module.
  • Split channel/user modes out of channel.c/client.c and into chanmode.c/usermode.c respectively.
  • Added init/teardown routines for channel/usermodes.
  • Changed the INVIS and OPER macros to have longer(:/) and more descriptive names.
File:
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/ithildin/modules/ircd/usermode.c

    r801 r803  
    11/* 
    2  * client.c: client structure management functions 
     2 * usermode.c: client (user) mode management functions 
    33 *  
    4  * Copyright 2002 the Ithildin Project. 
     4 * Copyright 2002-2007 the Ithildin Project. 
    55 * See the COPYING file for more information on licensing and use. 
    66 *  
    7  * This file provides mechanisms for creating/destroy client structures, as 
    8  * well as registering them on the server and setting/unsetting user modes on 
    9  * them. 
     7 * This file contains the generic code for manipulating user modes, as well 
     8 * as some base usermodes supported by the server. 
    109 */ 
    1110 
     
    1615IDSTRING(rcsid, "$Id$"); 
    1716 
    18 /* this creates a new client structure and places it in the 'conn' structure 
    19  * passed, if any.  If there is no conn structure, then this is either a remote 
    20  * client or a pseudo-client. */ 
    21 client_t *create_client(connection_t *conn) { 
    22     client_t *cp = calloc(1, sizeof(client_t)); 
    23     cp->conn = conn; 
    24  
    25     /* set connection time/etc */ 
    26     cp->ts = cp->last = me.now; 
    27     cp->orighost = cp->host; /* don't forget this one */ 
    28  
    29     cp->mdext = mdext_alloc(ircd.mdext.client); 
    30  
    31     if (conn != NULL) { 
    32         conn->cli = cp; 
    33         cp->pset = conn->cls->pset; 
    34         get_socket_address(isock_raddr(conn->sock), cp->ip, IPADDR_MAXLEN + 1, 
    35                 NULL); 
    36         ircd.stats.serv.unkclients++; 
    37     } else 
    38         cp->pset = LIST_FIRST(ircd.privileges.sets); 
    39  
    40     LIST_INSERT_HEAD(ircd.lists.clients, cp, lp); 
    41     return cp; 
    42 } 
    43  
    44 /* this destroys a client, and if it is our connection, also destroys the 
    45  * client's connection.  do *NOT* try to destroy connections after 
    46  * destroying their clients. */ 
    47 void destroy_client(client_t *cli, char *msg) { 
    48     struct chanlink *clp; 
    49     unsigned char *s; 
    50  
    51     if (cli->flags & IRCD_CLIENT_HISTORY) { 
    52         /* just return the memory */ 
    53         mdext_free(ircd.mdext.client, cli->mdext); 
    54         free(cli); 
    55         return; 
    56     } 
    57  
    58     if (CLIENT_REGISTERED(cli)) { /* only do this stuff for registered 
    59                                      clients */ 
    60         if (MYCLIENT(cli)) 
    61             hook_event(ircd.events.client_disconnect, cli); 
    62         hook_event(ircd.events.unregister_client, cli); 
    63  
    64         /* if the client hasn't been flagged as 'killed' (that is, destroyed by 
    65          * some other action) we propogate the quit message to other servers. 
    66          * whatever betide we send the quit to all local users! */ 
    67         if (!(cli->flags & IRCD_CLIENT_KILLED)) 
    68             sendto_serv_butone(cli->server, cli, NULL, NULL, "QUIT", ":%s", 
    69                     msg); 
    70  
    71         clp = LIST_FIRST(&cli->chans); 
    72         if (clp != NULL) { 
    73             /* if we are in channels */ 
    74             /* don't send a message to our user */ 
    75             if (MYCLIENT(cli)) 
    76                 ircd.sends[cli->conn->sock->fd] = 1; 
    77             sendto_common_channels(cli, NULL, "QUIT", ":%s", msg); 
    78             while (clp != NULL) { 
    79                 del_from_channel(cli, clp->chan, true); 
    80                 clp = LIST_FIRST(&cli->chans); 
    81             } 
    82         } 
    83  
    84         /* add them to the client history, and also sign them off. */ 
    85         client_add_history(cli); 
    86         /* and mark the client structure as a 'history' structure */ 
    87         cli->flags |= IRCD_CLIENT_HISTORY; 
    88  
    89         /* unset all their modes.  this allows any resource cleanup to be 
    90          * handled by individual mode handlers. */ 
    91         s = ircd.umodes.avail; 
    92         while (*s) 
    93             usermode_unset(*s++, cli, cli, NULL, NULL); 
    94  
    95         if (MYCLIENT(cli)) { 
    96             int i; 
    97  
    98             ircd.stats.serv.curclients--; 
    99             for (i = 0;i < ircd.sflag.size;i++) 
    100                 remove_from_send_flag(i, cli, true); 
    101         } 
    102         /* they would now be visible, except we're nerfing them. */ 
    103         ircd.stats.net.visclients--; 
    104         ircd.stats.net.curclients--; 
    105     } 
    106  
    107     /* Oops.  We have to delete clients from the hashtable even if they aren't 
    108      * registered.  Thanks dave. */ 
    109     if (*cli->nick != '\0') 
    110         hash_delete(ircd.hashes.client, cli); 
    111  
    112     if (cli->conn != NULL) { /* dump it if it's ours */ 
    113         if (!CLIENT_REGISTERED(cli)) 
    114             ircd.stats.serv.unkclients--; 
    115         cli->conn->cli = NULL; 
    116  
    117         /* If sendq_flush does not close the connection for us (it will 
    118          * return 0 if it does) then close the connection.  This ensures 
    119          * that we do best-effort work on dumping the last dribbly bits of 
    120          * the sendq out to the client if we can.  We do not try hard to 
    121          * make this work, though! */ 
    122         if (sendq_flush(cli->conn)) 
    123             destroy_connection(cli->conn, msg); 
    124     } 
    125  
    126     LIST_REMOVE(cli, lp); 
    127  
    128     if (!CLIENT_REGISTERED(cli)) { 
    129         /* if the client was registered then we need to preserve their client 
    130          * entry for the history list */ 
    131         mdext_free(ircd.mdext.client, cli->mdext); 
    132         free(cli); 
    133     } 
    134 } 
    135  
    136 /* this function changes a client's nickname, and performs all associations 
    137  * necessary (moving hash structures, and adding the old nickname into the 
    138  * client history).  It does *NOT* verify that 'to' is a suitable nickname, 
    139  * however, as that is left up to the caller. */ 
    140 void client_change_nick(client_t *cli, char *to) { 
    141     int casechng = !istrcmp(ircd.maps.nick, cli->nick, to); 
    142      
    143     /* check to see if this is just a case change.  if it is, istrcmp will 
    144      * return 0.  if it's a case change, we don't do anything except set the 
    145      * new nickname in the client structure. */ 
    146     if (*cli->nick != '\0' && !casechng) { 
    147         hash_delete(ircd.hashes.client, cli); 
    148  
    149         /* History entries for unregistered clients are extremely useless, 
    150          * and detrimental sometimes. */ 
    151         if (CLIENT_REGISTERED(cli)) 
    152             client_add_history(cli); 
    153     } 
    154  
    155     strncpy(cli->nick, to, NICKLEN); 
    156  
    157     if (!casechng) { 
    158         hash_insert(ircd.hashes.client, cli); 
    159  
    160         /* We only hook client_nick for registered clients, there is little 
    161          * value in hooking it for nonregistered folks. */ 
    162         if (CLIENT_REGISTERED(cli)) 
    163             hook_event(ircd.events.client_nick, cli); 
    164     } 
    165 } 
    166  
    167 /* find a client with the given name, but only if it is registered */ 
    168 client_t *find_client(char *name) { 
    169     client_t *cp; 
    170  
    171     if ((cp = find_client_any(name)) != NULL && CLIENT_REGISTERED(cp)) 
    172         return cp; 
    173     return NULL; 
    174 } 
    175  
    176 /* register a client, the appropriate stuff should already be filled in */ 
    177 int register_client(client_t *cli) { 
    178     connection_t *cp = cli->conn; 
    179     void **returns; /* hook returns */ 
    180     int x = 0; 
    181                  
    182     /* determine if it is okay for them to connect.  if it is, and we allow the 
    183      * connection through, let the rest of the network know before we do 
    184      * anything else! */ 
    185     if (MYCLIENT(cli)) { 
    186         /* only do this stuff if it's a real client, and not a fake one */ 
    187         if (cp != NULL) { 
    188             /* check to see if our client is okay */ 
    189             returns = hook_event(ircd.events.connection_registered, cp); 
    190             while (x < hook_num_returns) { 
    191                 if (returns[x] != NULL) {/* denied, with a reason */ 
    192                     destroy_client(cli, (char *)returns[x]); 
    193                         return IRCD_CONNECTION_CLOSED; /* not successful */ 
    194                 } 
    195                 x++; 
    196             } 
    197             /* remove our client from the unregistered list, and put it in 
    198              * the registered * list. */ 
    199             LIST_REMOVE(cp, lp); 
    200             LIST_INSERT_HEAD(ircd.connections.clients, cp, lp); 
    201             ircd.stats.serv.unkclients--; 
    202  
    203         } 
    204  
    205         cli->signon = cli->ts = me.now; 
    206         cli->hops = 0; /* our client, they're 0 hops from us <G> */ 
    207  
    208         /* only hook for local registration */ 
    209         hook_event(ircd.events.client_connect, cli); 
    210  
    211         /* clear the password after client_connect, assume that nothing else 
    212          * will need to hook it and use it anymore. */ 
    213         if (cp != NULL) { 
    214             /* clean out their password */ 
    215             if (cp->pass != NULL) { 
    216                 free(cp->pass); 
    217                 cp->pass = NULL; 
    218             } 
    219         } 
    220  
    221         /* accounting stuff */ 
    222         ircd.stats.serv.curclients++; 
    223         if (ircd.stats.serv.maxclients < ircd.stats.serv.curclients) 
    224             ircd.stats.serv.maxclients++; 
    225  
    226         /* now welcome our client */ 
    227         sendto_one(cli, RPL_FMT(cli, RPL_WELCOME), ircd.network_full, 
    228                 cli->nick, cli->user, cli->host); 
    229         sendto_one(cli, RPL_FMT(cli, RPL_YOURHOST), ircd.me->name, 
    230                 ircd.version); 
    231         sendto_one(cli, RPL_FMT(cli, RPL_CREATED), ircd.ascstart); 
    232         sendto_one(cli, RPL_FMT(cli, RPL_MYINFO), ircd.me->name, ircd.version, 
    233                 ircd.umodes.avail, ircd.cmodes.avail); 
    234         send_isupport(cli); 
    235  
    236     } else 
    237         /* remote clients won't have been added to the hash, do that now */ 
    238         hash_insert(ircd.hashes.client, cli); 
    239                                          
    240     ircd.stats.net.visclients++; /* clients are visible by default */ 
    241     ircd.stats.net.curclients++; 
    242     if (ircd.stats.net.maxclients < ircd.stats.net.curclients) 
    243         ircd.stats.net.maxclients++; 
    244  
    245     /* If the IP hasn't been explicitly filled in yet, set it to 0.0.0.0.  Ugh 
    246      * ugh ugh. :) */ 
    247     if (*cli->ip == '\0') 
    248         strcpy(cli->ip, "0.0.0.0"); 
    249  
    250     cli->flags |= IRCD_CLIENT_REGISTERED; 
    251  
    252     /* now introduce the client to our servers down the line, sptr should be 
    253      * the server the client was introduced from (possibly us) */ 
    254     LIST_FOREACH(cp, ircd.connections.servers, lp) { 
    255         if (cp->srv != sptr) 
    256             cp->proto->register_user(cp,  cli); 
    257     } 
    258  
    259     /* hook for all clients */ 
    260     hook_event(ircd.events.register_client, cli); 
    261     return 0; 
    262 } 
    263  
    264 int check_nickname(char *nick) { 
    265     char first[2] = { '\0', '\0' }; 
    266  
    267     first[0] = *nick; 
    268     if (!istr_okay(ircd.maps.nick_first, first) || 
    269             !istr_okay(ircd.maps.nick, nick)) 
    270         return 0; 
    271     return 1; 
    272 } 
    273  
    274 /* this takes a mask of just about any form as input and tries to turn it into 
    275  * something useable in the nick!user@host form.  It mangles the input, so be 
    276  * careful. */ 
    277 char *make_client_mask(char *mask) { 
    278     static char fmask[NICKLEN + USERLEN + HOSTLEN + 3]; 
    279     char *s, *nick, *user, *host; 
    280  
    281     s = mask; 
    282     nick = user = host = NULL; 
    283     if ((user = strchr(mask, '!')) != NULL) { 
    284         *user++ = '\0'; /* find the user portion .. */ 
    285         nick = s; /* it is nick!user.. format */ 
    286     } 
    287     if ((host = strchr((user != NULL ? user : s), '@')) != NULL) { 
    288         *host++ = '\0'; /* find the host portion */ 
    289         if (user == NULL) /* it is user@host form */ 
    290             user = s; /* so s is the user */ 
    291     } else if (user == NULL && strpbrk(s, ".:") != NULL) 
    292         host = s; /* it is a plain host */ 
    293     else 
    294         nick = s; /* it is a plain nickname */ 
    295  
    296     /* okay, we now have nick, user, and host.  make a mask. */ 
    297     sprintf(fmask, "%s!%s@%s", 
    298             (nick == NULL || *nick == '\0' ? "*" : nick), 
    299             (user == NULL || *user == '\0' ? "*" : user), 
    300             (host == NULL || *host == '\0' ? "*" : host)); 
    301  
    302     return fmask; 
    303 } 
    304  
    305 int client_check_access(client_t *from, client_t *to, char *ext, event_t *ep) { 
    306     struct client_check_args cca; 
    307  
    308     cca.from = from; 
    309     cca.to = to; 
    310     cca.extra = ext; 
    311  
    312     return hook_cond_event(ep, &cca); 
    313 } 
    314  
    315 /* mode goodies below here */ 
     17static USERMODE_FUNC(mode_set_counter); 
     18 
     19/* This function either initializes the usermode system or handles updates 
     20 * in the case of a reload. */ 
     21void usermode_init(bool reload) { 
     22    uint64_t ui64 = 1; 
     23    unsigned char c; 
     24 
     25#define MODELOOP(from, to) do {                                               \ 
     26    for (c = from;c <= to;c++, ui64 <<= 1) {                                  \ 
     27        ircd.umodes.modes[c].mode = c;                                        \ 
     28        ircd.umodes.modes[c].mask = ui64;                                     \ 
     29        ircd.umodes.modes[c].avail = 1;                                       \ 
     30    }                                                                         \ 
     31} while (0) 
     32 
     33    if (reload == false) { 
     34        MODELOOP('a', 'z'); /* set a-z modes */ 
     35        MODELOOP('A', 'Z'); /* and A-Z modes */ 
     36        MODELOOP('0', '9'); /* and 0-9 modes */ 
     37 
     38        /* we know that all modes are free.  grab i, o, and s */ 
     39        ircd.umodes.mode_invis = usermode_request('i', &c, USERMODE_FL_GLOBAL, 
     40                -1, mode_set_counter); 
     41        ircd.umodes.mode_oper = usermode_request('o', &c, USERMODE_FL_GLOBAL, 
     42                ircd.sflag.ops, mode_set_counter); 
     43        ircd.umodes.mode_servmsg = usermode_request('s', &c, 0, 
     44                ircd.sflag.servmsg, NULL); 
     45    } else { 
     46        /* Be sure to update the function address for this channel mode in 
     47         * case it moved.. */ 
     48        usermode_update_func(ircd.umodes.mode_invis, mode_set_counter); 
     49        usermode_update_func(ircd.umodes.mode_oper, mode_set_counter); 
     50        usermode_update_func(ircd.umodes.mode_servmsg, mode_set_counter); 
     51    } 
     52} 
     53 
     54void usermode_deinit(bool reload) { 
     55    if (reload == false) { 
     56        /* dump our modes */ 
     57        usermode_release(ircd.umodes.mode_invis); 
     58        usermode_release(ircd.umodes.mode_oper); 
     59        usermode_release(ircd.umodes.mode_servmsg); 
     60    } 
     61} 
     62 
    31663uint64_t usermode_request(unsigned char suggested, unsigned char *actual, 
    31764        int flags, int sflag, usermode_func changefunc) { 
     
    391138 
    392139    if (md->avail || !md->mask) 
    393         return 0; 
     140        return; 
    394141 
    395142    md->changefunc = changefunc; 
     
    478225    /* if they're local, not opered, and the mode is an oper mode, don't let 
    479226     * them set it. */ 
    480     if (MYCLIENT(on) && !OPER(on) && md->flags & USERMODE_FL_OPER) 
     227    if (MYCLIENT(on) && !CLIENT_OPERATOR(on) && md->flags & USERMODE_FL_OPER) 
    481228        return 0; /* no no. */ 
    482229 
     
    519266} 
    520267 
    521 static void client_remove_history(client_t *); 
    522  
    523 /* this function adds a client to the client_history list.  If we've maxed out 
    524  * on entries (we use the client_history hash table to examine this) we start 
    525  * back at the first entry and overwrite our way forward again. */ 
    526 struct client_history *client_add_history(client_t *cli) { 
    527     struct client_history *chp = NULL; 
    528  
    529     /* only one history entry may point to a client at any time */ 
    530     if (cli->hist != NULL) 
    531         client_remove_history(cli); 
    532  
    533     if ((chp = client_find_history(cli->nick)) != NULL) 
    534         /* I was having this not create a new history structure, but I've had 
    535          * so many problems with this code (little bugs here and there) that 
    536          * I've decided to go for the slightly less efficient route.  I'm tired 
    537          * of trying little tricks in this code to keep it fast. */ 
    538         client_remove_history(chp->cli); 
    539  
    540    if ((hashtable_count(ircd.hashes.client_history) >= 
    541              hashtable_size(ircd.hashes.client_history)) && 
    542             (hashtable_size(ircd.hashes.client_history) >= 
    543              hashtable_size(ircd.hashes.client))) { 
    544         /* this is the tricky one.  our table is full so we need to 
    545          * overwrite old entries (maybe).  if the client table is larger 
    546          * than the history table then we also let the history table grow, 
    547          * but if the two are * the same size we need to pop the oldest 
    548          * entry off. */ 
    549  
    550         chp = TAILQ_LAST(ircd.lists.client_history, client_history_list); 
    551         client_remove_history(chp->cli); 
    552     } 
    553  
    554         chp = calloc(1, sizeof(struct client_history)); 
    555  
    556     /* fill it in ... */ 
    557     strcpy(chp->nick, cli->nick); 
    558     strcpy(chp->serv, cli->server->name); 
    559     chp->cli = cli; 
    560     cli->hist = chp; 
    561     chp->signoff = me.now; 
    562  
    563     /* insert it in the hash */ 
    564         hash_insert(ircd.hashes.client_history, chp); 
    565     TAILQ_INSERT_HEAD(ircd.lists.client_history, chp, lp); 
    566  
    567     return chp; 
    568 } 
    569  
    570 /* remove the client history data for a client.  if the client structure is 
    571  * marked as 'signed off' we also free the client structure as well. */ 
    572 static void client_remove_history(client_t *cli) { 
    573  
    574     if (cli->hist == NULL) { 
    575         log_error("trying to delete history for a client which has none."); 
    576         return; 
    577     } 
    578  
    579     assert(cli == cli->hist->cli); 
    580  
    581     hash_delete(ircd.hashes.client_history, cli->hist); 
    582     TAILQ_REMOVE(ircd.lists.client_history, cli->hist, lp); 
    583     free(cli->hist); 
    584      
    585     if (cli->flags & IRCD_CLIENT_HISTORY) 
    586         destroy_client(cli, NULL); 
    587     else 
    588         cli->hist = NULL; 
    589 } 
    590  
    591 /* this function attempts to find the client with the given nick, first by 
    592  * looking for the actual client, then by looking in history for it.  it 
    593  * returns an online client structure or NULL, no matter what. */ 
    594 client_t *client_get_history(char *nick, time_t limit) { 
    595     struct client_history *chp; 
    596     client_t *cli; 
    597  
    598     if ((cli = find_client(nick)) != NULL) 
    599         return cli; 
    600  
    601     /* no luck?  try the client history. */ 
    602     chp = client_find_history(nick); 
    603     /* if we found it and they signed off at or after the current time minus 
    604      * the time limit, return the entry. */ 
    605     if (chp != NULL && !(chp->cli->flags & IRCD_CLIENT_HISTORY) && 
    606             chp->signoff >= (limit ? me.now - limit : 0)) 
    607         return chp->cli; 
    608  
    609     return NULL; 
    610 } 
    611  
    612 char **client_mdext_iter(char **last) { 
    613     client_t *cp = *(client_t **)last; 
    614     static int started = 0; 
    615  
    616     if (cp == NULL) { 
    617         if (started) { 
    618             started = 0; 
    619             return NULL; 
    620         } 
    621         cp = LIST_FIRST(ircd.lists.clients); 
    622         started = 1; 
    623     } 
    624     if (cp == NULL) 
    625         return NULL; 
    626  
    627     *(client_t **)last = LIST_NEXT(cp, lp); 
    628     return (char **)&cp->mdext; 
    629 } 
    630  
    631 int nickcmp(char *one, char *two, size_t len __UNUSED) { 
    632         return istrcmp(ircd.maps.nick, one, two); 
     268/* *************************************************************************** 
     269 * Code specific to individual usermodes 
     270 * ***************************************************************************/ 
     271static USERMODE_FUNC(mode_set_counter) { 
     272    int i; 
     273 
     274    switch (mode) { 
     275        case 'i': 
     276            if (set) 
     277                ircd.stats.net.visclients--; 
     278            else 
     279                ircd.stats.net.visclients++; 
     280            break; 
     281        case 'o': 
     282            /* if this is our client, isn't set +o, and doesn't have the 
     283             * privilege to do, deny. */ 
     284            if (MYCLIENT(cli) && by == cli && !CLIENT_OPERATOR(cli) && 
     285                    !BPRIV(cli, ircd.privileges.priv_operator)) 
     286                return 0; 
     287            if (set) { 
     288                ircd.stats.opers++; 
     289                if (!MYCLIENT(cli)) 
     290                    /* change them to the default operator privilege set */ 
     291                    cli->pset = ircd.privileges.oper_set; 
     292                hook_event(ircd.events.client_oper, cli); 
     293            } else { 
     294                /* remove them from any operator-only send flags, also remove 
     295                 * any operator-only modes they might have set */ 
     296                if (MYCLIENT(cli)) { 
     297                    unsigned char *s; 
     298                    for (s = ircd.umodes.avail;*s != '\0';s++) { 
     299                        if (ircd.umodes.modes[*s].flags & USERMODE_FL_OPER && 
     300                                !(ircd.umodes.modes[*s].flags & 
     301                                    USERMODE_FL_PRESERVE)) 
     302                            usermode_unset(*s, cli, cli, NULL, NULL); 
     303                    } 
     304                    for (i = 0;i < ircd.sflag.size;i++) { 
     305                        if (ircd.sflag.flags[i].flags & 
     306                                SEND_LEVEL_OPERATOR && 
     307                            !(ircd.sflag.flags[i].flags & 
     308                                SEND_LEVEL_PRESERVE)) 
     309                            remove_from_send_flag(i, cli, true); 
     310                    } 
     311                } 
     312                ircd.stats.opers--; 
     313                hook_event(ircd.events.client_deoper, cli); 
     314            } 
     315            break; 
     316    } 
     317 
     318    return 1; 
    633319} 
    634320 
Note: See TracChangeset for help on using the changeset viewer.