Changeset 803 for trunk/ithildin/modules/ircd/usermode.c
- Timestamp:
- 05/31/07 15:15:23 (5 years ago)
- File:
-
- 1 copied
-
trunk/ithildin/modules/ircd/usermode.c (copied) (copied from trunk/ithildin/modules/ircd/client.c) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/ithildin/modules/ircd/usermode.c
r801 r803 1 1 /* 2 * client.c: client structure management functions2 * usermode.c: client (user) mode management functions 3 3 * 4 * Copyright 2002 the Ithildin Project.4 * Copyright 2002-2007 the Ithildin Project. 5 5 * See the COPYING file for more information on licensing and use. 6 6 * 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. 10 9 */ 11 10 … … 16 15 IDSTRING(rcsid, "$Id$"); 17 16 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 */ 17 static USERMODE_FUNC(mode_set_counter); 18 19 /* This function either initializes the usermode system or handles updates 20 * in the case of a reload. */ 21 void 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 54 void 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 316 63 uint64_t usermode_request(unsigned char suggested, unsigned char *actual, 317 64 int flags, int sflag, usermode_func changefunc) { … … 391 138 392 139 if (md->avail || !md->mask) 393 return 0;140 return; 394 141 395 142 md->changefunc = changefunc; … … 478 225 /* if they're local, not opered, and the mode is an oper mode, don't let 479 226 * 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) 481 228 return 0; /* no no. */ 482 229 … … 519 266 } 520 267 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 * ***************************************************************************/ 271 static 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; 633 319 } 634 320
Note: See TracChangeset
for help on using the changeset viewer.
