diff options
author | svn svn@31f1291d-b8d6-0310-a050-a5561fc1590b <svn svn@31f1291d-b8d6-0310-a050-a5561fc1590b@5417fbe8-f217-4b02-8779-1006273d7864> | 2004-03-28 21:59:56 +0000 |
---|---|---|
committer | svn svn@31f1291d-b8d6-0310-a050-a5561fc1590b <svn svn@31f1291d-b8d6-0310-a050-a5561fc1590b@5417fbe8-f217-4b02-8779-1006273d7864> | 2004-03-28 21:59:56 +0000 |
commit | 55bf4dbcabf378e9472b7d31d6edf87f6ac853e9 (patch) | |
tree | 7a9454ea6b8750256e242cf6d5fba3ca7a4b5044 /users.c |
Initial Anope Import
git-svn-id: svn://svn.anope.org/anope/trunk@1 31f1291d-b8d6-0310-a050-a5561fc1590b
git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@1 5417fbe8-f217-4b02-8779-1006273d7864
Diffstat (limited to 'users.c')
-rw-r--r-- | users.c | 1136 |
1 files changed, 1136 insertions, 0 deletions
diff --git a/users.c b/users.c new file mode 100644 index 000000000..31810c8c4 --- /dev/null +++ b/users.c @@ -0,0 +1,1136 @@ +/* Routines to maintain a list of online users. + * + * (C) 2003 Anope Team + * Contact us at info@anope.org + * + * Please read COPYING and README for furhter details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + * + * $Id: users.c,v 1.37 2004/02/18 19:00:33 rob Exp $ + * + */ + +#include "services.h" + +#define HASH(nick) (((nick)[0]&31)<<5 | ((nick)[1]&31)) +User *userlist[1024]; + +int32 usercnt = 0, opcnt = 0, maxusercnt = 0; +time_t maxusertime; + +static unsigned long umodes[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, UMODE_A, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#if defined(IRC_ULTIMATE) || defined(IRC_ULTIMATE3) + UMODE_P, +#else + 0, +#endif + 0, +#if defined(IRC_BAHAMUT) || defined(IRC_ULTIMATE) + UMODE_R, +#else + 0, +#endif + 0, 0, 0, 0, 0, 0, 0, +#ifdef IRC_ULTIMATE3 + UMODE_Z, +#else + 0, +#endif + 0, 0, 0, 0, 0, + 0, UMODE_a, 0, 0, 0, 0, 0, +#ifdef IRC_DREAMFORGE + UMODE_g, +#else + 0, +#endif + UMODE_h, UMODE_i, 0, 0, 0, 0, 0, UMODE_o, +#if defined(IRC_ULTIMATE) || defined(IRC_ULTIMATE3) + UMODE_p, +#else + 0, +#endif + 0, UMODE_r, 0, 0, 0, 0, UMODE_w, +#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) || defined(IRC_VIAGRA) + UMODE_x, +#else + 0, +#endif + 0, 0, 0, 0, 0, 0, 0 +}; + +/*************************************************************************/ +/*************************************************************************/ + +/* Allocate a new User structure, fill in basic values, link it to the + * overall list, and return it. Always successful. + */ + +static User *new_user(const char *nick) +{ + User *user, **list; + + user = scalloc(sizeof(User), 1); + if (!nick) + nick = ""; + strscpy(user->nick, nick, NICKMAX); + list = &userlist[HASH(user->nick)]; + user->next = *list; + if (*list) + (*list)->prev = user; + *list = user; + user->na = findnick(nick); + if (user->na) + user->na->u = user; + usercnt++; + if (usercnt > maxusercnt) { + maxusercnt = usercnt; + maxusertime = time(NULL); + if (LogMaxUsers) + alog("user: New maximum user count: %d", maxusercnt); + } + user->isSuperAdmin = 0; /* always set SuperAdmin to 0 for new users */ + return user; +} + +/*************************************************************************/ + +/* Change the nickname of a user, and move pointers as necessary. */ + +static void change_user_nick(User * user, const char *nick) +{ + User **list; + int is_same = (!stricmp(user->nick, nick) ? 1 : 0); + + if (user->prev) + user->prev->next = user->next; + else + userlist[HASH(user->nick)] = user->next; + if (user->next) + user->next->prev = user->prev; + user->nick[1] = 0; /* paranoia for zero-length nicks */ + strscpy(user->nick, nick, NICKMAX); + list = &userlist[HASH(user->nick)]; + user->next = *list; + user->prev = NULL; + if (*list) + (*list)->prev = user; + *list = user; + + /* Only if old and new nick aren't the same; no need to waste time */ + if (!is_same) { + if (user->na) + user->na->u = NULL; + user->na = findnick(nick); + if (user->na) + user->na->u = user; + } +} + +/*************************************************************************/ + +#ifdef HAS_VHOST + +static void update_host(User * user) +{ + if (user->na && (nick_identified(user) + || (!(user->na->nc->flags & NI_SECURE) + && nick_recognized(user)))) { + if (user->na->last_usermask) + free(user->na->last_usermask); + + user->na->last_usermask = + smalloc(strlen(GetIdent(user)) + strlen(GetHost(user)) + 2); + sprintf(user->na->last_usermask, "%s@%s", GetIdent(user), + GetHost(user)); + } + + if (debug) + alog("debug: %s changes its host to %s", user->nick, + GetHost(user)); +} + +#endif + +/*************************************************************************/ + +#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) || defined(IRC_VIAGRA) || defined(IRC_PTLINK) + +/* Change the (virtual) hostname of a user. */ + +void change_user_host(User * user, const char *host) +{ + if (user->vhost) + free(user->vhost); + user->vhost = sstrdup(host); + + if (debug) + alog("debug: %s changes its vhost to %s", user->nick, host); + + + + update_host(user); +} + +#endif +/*************************************************************************/ + +#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_VIAGRA) || defined(IRC_PTLINK) +/* Change the realname of a user. */ + +void change_user_realname(User * user, const char *realname) +{ + if (user->realname) + free(user->realname); + user->realname = sstrdup(realname); + + if (user->na && (nick_identified(user) + || (!(user->na->nc->flags & NI_SECURE) + && nick_recognized(user)))) { + if (user->na->last_realname) + free(user->na->last_realname); + user->na->last_realname = sstrdup(realname); + } + + if (debug) + alog("debug: %s changes its realname to %s", user->nick, realname); +} + + +/*************************************************************************/ + +/* Change the username of a user. */ + +void change_user_username(User * user, const char *username) +{ + if (user->username) + free(user->username); + user->username = sstrdup(username); + if (user->na && (nick_identified(user) + || (!(user->na->nc->flags & NI_SECURE) + && nick_recognized(user)))) { + if (user->na->last_usermask) + free(user->na->last_usermask); + + user->na->last_usermask = + smalloc(strlen(GetIdent(user)) + strlen(GetHost(user)) + 2); + sprintf(user->na->last_usermask, "%s@%s", GetIdent(user), + GetHost(user)); + } + if (debug) + alog("debug: %s changes its username to %s", user->nick, username); +} + +#endif + +/*************************************************************************/ + +void set_umode(User * user, int ac, char **av) +{ + int add = 1; /* 1 if adding modes, 0 if deleting */ + char *modes = av[0]; + + ac--; + + if (debug) + alog("debug: Changing mode for %s to %s", user->nick, modes); + + while (*modes) { + + add ? (user->mode |= umodes[(int) *modes]) : (user->mode &= + ~umodes[(int) + *modes]); + + switch (*modes++) { + case '+': + add = 1; + break; + case '-': + add = 0; + break; +#if defined(IRC_ULTIMATE) || defined(IRC_ULTIMATE3) + case 'a': + if (add && !is_services_oper(user)) { + send_cmd(ServerName, "SVSMODE %s -a", user->nick); + user->mode &= ~UMODE_a; + } + break; + case 'P': + if (add && !is_services_admin(user)) { + send_cmd(ServerName, "SVSMODE %s -P", user->nick); + user->mode &= ~UMODE_P; + } + break; +#endif +#if defined(IRC_ULTIMATE) + case 'R': + if (add && !is_services_root(user)) { + send_cmd(ServerName, "SVSMODE %s -R", user->nick); + user->mode &= ~UMODE_R; + } + break; +#endif +#if defined(IRC_ULTIMATE3) + case 'Z': + if (add && !is_services_root(user)) { + send_cmd(ServerName, "SVSMODE %s -Z", user->nick); + user->mode &= ~UMODE_Z; + } + break; +#endif + case 'd': + if (ac == 0) { +#if !defined(IRC_ULTIMATE) && !defined(IRC_UNREAL) + alog("user: umode +d with no parameter (?) for user %s", + user->nick); +#endif + break; + } + + ac--; + av++; + user->svid = strtoul(*av, NULL, 0); + break; + case 'o': + if (add) { + opcnt++; + + if (WallOper) + wallops(s_OperServ, "\2%s\2 is now an IRC operator.", + user->nick); + display_news(user, NEWS_OPER); +#if defined(IRC_PTLINK) + if (is_services_admin(user)) { + send_cmd(ServerName, "SVSMODE %s +a", user->nick); + user->mode |= UMODE_a; + } +#endif +#if defined(IRC_ULTIMATE) || defined(IRC_ULTIMATE3) + if (is_services_oper(user)) { + send_cmd(ServerName, "SVSMODE %s +a", user->nick); + user->mode |= UMODE_a; + } + + if (is_services_admin(user)) { + send_cmd(ServerName, "SVSMODE %s +P", user->nick); + user->mode |= UMODE_P; + } +#endif + +#ifdef IRC_ULTIMATE + if (is_services_root(user)) { + send_cmd(ServerName, "SVSMODE %s +R", user->nick); + user->mode |= UMODE_R; + } +#endif +#ifdef IRC_ULTIMATE3 + if (is_services_root(user)) { + send_cmd(ServerName, "SVSMODE %s +Z", user->nick); + user->mode |= UMODE_Z; + } +#endif + } else { + opcnt--; + } + break; + case 'r': + if (add && !nick_identified(user)) { + send_cmd(ServerName, "SVSMODE %s -r", user->nick); + user->mode &= ~UMODE_r; + } + break; +#if defined(IRC_ULTIMATE) || defined(IRC_UNREAL) || defined(IRC_ULTIMATE3) || defined(IRC_VIAGRA) + case 'x': + update_host(user); + break; +#endif + } + } +} + +/*************************************************************************/ + +/* Remove and free a User structure. */ + +static void delete_user(User * user) +{ + struct u_chanlist *c, *c2; + struct u_chaninfolist *ci, *ci2; + + if (LogUsers) { +#ifdef HAS_VHOST + alog("LOGUSERS: %s (%s@%s => %s) (%s) left the network (%s).", + user->nick, user->username, user->host, + (user->vhost ? user->vhost : "(none)"), user->realname, + user->server); +#else + alog("LOGUSERS: %s (%s@%s) (%s) left the network (%s).", + user->nick, user->username, user->host, + user->realname, user->server); +#endif + } + + if (debug >= 2) + alog("debug: delete_user() called"); + usercnt--; + if (is_oper(user)) + opcnt--; + if (debug >= 2) + alog("debug: delete_user(): free user data"); + free(user->username); + free(user->host); +#ifdef HAS_VHOST + if (user->vhost) + free(user->vhost); +#endif + free(user->realname); + free(user->server); + if (debug >= 2) + alog("debug: delete_user(): remove from channels"); + c = user->chans; + while (c) { + c2 = c->next; + chan_deluser(user, c->chan); + free(c); + c = c2; + } + /* This called only here now */ + cancel_user(user); + if (user->na) + user->na->u = NULL; + if (debug >= 2) + alog("debug: delete_user(): free founder data"); + ci = user->founder_chans; + while (ci) { + ci2 = ci->next; + free(ci); + ci = ci2; + } + if (debug >= 2) + alog("debug: delete_user(): delete from list"); + if (user->prev) + user->prev->next = user->next; + else + userlist[HASH(user->nick)] = user->next; + if (user->next) + user->next->prev = user->prev; + if (debug >= 2) + alog("debug: delete_user(): free user structure"); + free(user); + if (debug >= 2) + alog("debug: delete_user() done"); +} + +/*************************************************************************/ +/*************************************************************************/ + +/* Return statistics. Pointers are assumed to be valid. */ + +void get_user_stats(long *nusers, long *memuse) +{ + long count = 0, mem = 0; + int i; + User *user; + struct u_chanlist *uc; + struct u_chaninfolist *uci; + + for (i = 0; i < 1024; i++) { + for (user = userlist[i]; user; user = user->next) { + count++; + mem += sizeof(*user); + if (user->username) + mem += strlen(user->username) + 1; + if (user->host) + mem += strlen(user->host) + 1; +#ifdef HAS_VHOST + if (user->vhost) + mem += strlen(user->vhost) + 1; +#endif + if (user->realname) + mem += strlen(user->realname) + 1; + if (user->server) + mem += strlen(user->server) + 1; + for (uc = user->chans; uc; uc = uc->next) + mem += sizeof(*uc); + for (uci = user->founder_chans; uci; uci = uci->next) + mem += sizeof(*uci); + } + } + *nusers = count; + *memuse = mem; +} + +/*************************************************************************/ + +/* Find a user by nick. Return NULL if user could not be found. */ + +User *finduser(const char *nick) +{ + User *user; + + if (debug >= 3) + alog("debug: finduser(%p)", nick); + user = userlist[HASH(nick)]; + while (user && stricmp(user->nick, nick) != 0) + user = user->next; + if (debug >= 3) + alog("debug: finduser(%s) -> %p", nick, user); + return user; +} + +/*************************************************************************/ + +/* Iterate over all users in the user list. Return NULL at end of list. */ + +static User *current; +static int next_index; + +User *firstuser(void) +{ + next_index = 0; + while (next_index < 1024 && current == NULL) + current = userlist[next_index++]; + if (debug) + alog("debug: firstuser() returning %s", + current ? current->nick : "NULL (end of list)"); + return current; +} + +User *nextuser(void) +{ + if (current) + current = current->next; + if (!current && next_index < 1024) { + while (next_index < 1024 && current == NULL) + current = userlist[next_index++]; + } + if (debug) + alog("debug: nextuser() returning %s", + current ? current->nick : "NULL (end of list)"); + return current; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* Handle a server NICK command. */ + +User *do_nick(const char *source, char *nick, char *username, char *host, + char *server, char *realname, time_t ts, uint32 svid, ...) +{ + User *user; + + char *tmp = NULL; + NickAlias *old_na; /* Old nick rec */ + int nc_changed = 1; /* Did nick core change? */ + int status = 0; /* Status to apply */ + char mask[USERMAX + HOSTMAX + 2]; + + if (!*source) { +#ifdef HAS_NICKIP + char ipbuf[16]; + struct in_addr addr; + uint32 ip; +#endif +#ifdef HAS_VHOST + char *vhost = NULL; +#endif +#if defined(HAS_NICKIP) || defined(HAS_NICKVHOST) + va_list args; + va_start(args, svid); +#endif + +#ifdef HAS_NICKIP + ip = va_arg(args, uint32); +#endif + +#ifdef HAS_NICKVHOST + vhost = va_arg(args, char *); + if (!strcmp(vhost, "*")) { + vhost = NULL; + if (debug) + alog("debug: new user with no vhost in NICK command: %s", + nick); + } +#endif + + /* This is a new user; create a User structure for it. */ + if (debug) + alog("debug: new user: %s", nick); + + if (LogUsers) { + /** + * Ugly swap routine for Flop's bug :) + **/ + tmp = strchr(realname, '%'); + while (tmp) { + *tmp = '-'; + tmp = strchr(realname, '%'); + } + /** + * End of ugly swap + **/ + +#ifdef HAS_NICKIP + addr.s_addr = htonl(ip); + ntoa(addr, ipbuf, sizeof(ipbuf)); +#endif + +#ifdef HAS_NICKVHOST +# ifdef HAS_NICKIP + alog("LOGUSERS: %s (%s@%s => %s) (%s) [%s] connected to the network (%s).", nick, username, host, vhost, realname, ipbuf, server); +# else + alog("LOGUSERS: %s (%s@%s => %s) (%s) connected to the network (%s).", nick, username, host, vhost, realname, server); +# endif +#else +# ifdef HAS_NICKIP + alog("LOGUSERS: %s (%s@%s) (%s) [%s] connected to the network (%s).", nick, username, host, realname, ipbuf, server); +# else + alog("LOGUSERS: %s (%s@%s) (%s) connected to the network (%s).", nick, username, host, realname, server); +# endif +#endif + } + + /* We used to ignore the ~ which a lot of ircd's use to indicate no + * identd response. That caused channel bans to break, so now we + * just take what the server gives us. People are still encouraged + * to read the RFCs and stop doing anything to usernames depending + * on the result of an identd lookup. + */ + + /* First check for AKILLs. */ + /* DONT just return null if its an akill match anymore - yes its more efficent to, however, now that ircd's are + * starting to use things like E/F lines, we cant be 100% sure the client will be removed from the network :/ + * as such, create a user_struct, and if the client is removed, we'll delete it again when the QUIT notice + * comes in from the ircd. + **/ + if (check_akill(nick, username, host, +#ifdef HAS_NICKVHOST + vhost, +#else + NULL, +#endif +#ifdef HAS_NICKIP + ipbuf)) { +#else + NULL)) { +#endif +/* return NULL; */ + } + +/** + * DefCon AKILL system, if we want to akill all connecting user's here's where to do it + * then force check_akill again on them... + **/ + if (checkDefCon(DEFCON_AKILL_NEW_CLIENTS)) { + strncpy(mask, "*@", 3); + strncat(mask, host, HOSTMAX); + alog("DEFCON: adding akill for %s", mask); + add_akill(NULL, mask, s_OperServ, + time(NULL) + dotime(DefConAKILL), + DefConAkillReason ? DefConAkillReason : + "DEFCON AKILL"); + if (check_akill(nick, username, host, +#ifdef HAS_NICKVHOST + vhost, +#else + NULL, +#endif +#ifdef HAS_NICKIP + ipbuf)) { +#else + NULL)) { +#endif +/* return NULL; */ + } + } +#ifdef IRC_BAHAMUT + /* Next for SGLINEs */ + if (check_sgline(nick, realname)) + return NULL; +#endif + + /* And for SQLINEs */ + if (check_sqline(nick, 0)) + return NULL; + +#ifndef STREAMLINED + /* Now check for session limits */ + if (LimitSessions && !add_session(nick, host)) + return NULL; +#endif + + /* And finally, for proxy ;) */ +#ifdef USE_THREADS +# ifdef HAS_NICKIP + if (ProxyDetect && proxy_check(nick, host, ip)) +# else + if (ProxyDetect && proxy_check(nick, host, 0)) +# endif + return NULL; +#endif + + /* Allocate User structure and fill it in. */ + user = new_user(nick); + user->username = sstrdup(username); + user->host = sstrdup(host); + user->server = sstrdup(server); + user->realname = sstrdup(realname); + user->timestamp = ts; + user->my_signon = time(NULL); + +#ifdef HAS_VHOST + user->vhost = vhost ? sstrdup(vhost) : sstrdup(host); +#endif + + if (CheckClones) { + /* Check to see if it looks like clones. */ + check_clones(user); + } + + if (svid == 0) { + display_news(user, NEWS_LOGON); + display_news(user, NEWS_RANDOM); + } + + if (svid == ts && user->na) { + /* Timestamp and svid match, and nick is registered; automagically identify the nick */ + user->svid = svid; + user->na->status |= NS_IDENTIFIED; + check_memos(user); + nc_changed = 0; + } else if (svid != 1) { + /* Resets the svid because it doesn't match */ + user->svid = 1; +#ifdef IRC_BAHAMUT + send_cmd(ServerName, "SVSMODE %s %lu +d 1", user->nick, + user->timestamp); +#else +#ifndef IRC_PTLINK + send_cmd(ServerName, "SVSMODE %s +d 1", user->nick); +#endif +#endif + } else { + user->svid = 1; + } + + } else { + /* An old user changing nicks. */ + user = finduser(source); + if (!user) { + alog("user: NICK from nonexistent nick %s", source); + return NULL; + } + user->isSuperAdmin = 0; /* Dont let people nick change and stay SuperAdmins */ + if (debug) + alog("debug: %s changes nick to %s", source, nick); + + if (LogUsers) { +#ifdef HAS_VHOST + alog("LOGUSERS: %s (%s@%s => %s) (%s) changed his nick to %s (%s).", user->nick, user->username, user->host, (user->vhost ? user->vhost : "(none)"), user->realname, nick, user->server); +#else + alog("LOGUSERS: %s (%s@%s) (%s) changed his nick to %s (%s).", + user->nick, user->username, user->host, + user->realname, nick, user->server); +#endif + } + + user->timestamp = ts; + + if (stricmp(nick, user->nick) == 0) { + /* No need to redo things */ + change_user_nick(user, nick); + nc_changed = 0; + } else { + /* Update this only if nicks aren't the same */ + user->my_signon = time(NULL); + + old_na = user->na; + if (old_na) { + if (nick_recognized(user)) + user->na->last_seen = time(NULL); + status = old_na->status & NS_TRANSGROUP; + cancel_user(user); + } + + change_user_nick(user, nick); + + if ((old_na ? old_na->nc : NULL) == + (user->na ? user->na->nc : NULL)) + nc_changed = 0; + + if (!nc_changed && (user->na)) + user->na->status |= status; + else { +#if !defined(IRC_BAHAMUT) && !defined(IRC_PTLINK) + /* Because on Bahamut it would be already -r */ + change_user_mode(user, "-r+d", "1"); +#else + change_user_mode(user, "+d", "1"); +#endif + } + } + + if (!is_oper(user) && check_sqline(user->nick, 1)) + return NULL; + + } /* if (!*source) */ + + if (nc_changed || !nick_recognized(user)) { + if (validate_user(user)) + check_memos(user); + } else { + if (nick_identified(user)) { + user->na->last_seen = time(NULL); + + if (user->na->last_usermask) + free(user->na->last_usermask); + user->na->last_usermask = + smalloc(strlen(GetIdent(user)) + strlen(GetHost(user)) + + 2); + sprintf(user->na->last_usermask, "%s@%s", GetIdent(user), + GetHost(user)); + +#ifdef IRC_PTLINK + change_user_mode(user, "+r", NULL); +#endif + +#if !defined(IRC_BAHAMUT) && !defined(IRC_PTLINK) + if (user->svid != user->timestamp) { + char tsbuf[16]; + snprintf(tsbuf, sizeof(tsbuf), "%lu", user->timestamp); + change_user_mode(user, "+rd", tsbuf); + } else { + change_user_mode(user, "+r", NULL); + } +#endif + + + alog("%s: %s!%s@%s automatically identified for nick %s", + s_NickServ, user->nick, user->username, GetHost(user), + user->nick); + } + } + +/* Bahamut sets -r on every nick changes, so we must test it even if nc_changed == 0 */ +#ifdef IRC_BAHAMUT + if (nick_identified(user)) { + if (user->svid != user->timestamp) { + char tsbuf[16]; + snprintf(tsbuf, sizeof(tsbuf), "%lu", user->timestamp); + change_user_mode(user, "+rd", tsbuf); + } else { + change_user_mode(user, "+r", NULL); + } + } +#endif + + return user; +} + +/*************************************************************************/ + +/* Handle a MODE command for a user. + * av[0] = nick to change mode for + * av[1] = modes + */ + +void do_umode(const char *source, int ac, char **av) +{ + User *user; + + if (stricmp(source, av[0]) != 0) { + alog("user: MODE %s %s from different nick %s!", av[0], av[1], + source); + wallops(NULL, "%s attempted to change mode %s for %s", source, + av[1], av[0]); + return; + } + + user = finduser(source); + if (!user) { + alog("user: MODE %s for nonexistent nick %s: %s", av[1], source, + merge_args(ac, av)); + return; + } + + set_umode(user, ac - 1, &av[1]); +} + +/*************************************************************************/ + +/* Handle a QUIT command. + * av[0] = reason + */ + +void do_quit(const char *source, int ac, char **av) +{ + User *user; + NickAlias *na; + + user = finduser(source); + if (!user) { + alog("user: QUIT from nonexistent user %s: %s", source, + merge_args(ac, av)); + return; + } + if (debug) + alog("debug: %s quits", source); + if ((na = user->na) && (!(na->status & NS_VERBOTEN)) + && (na->status & (NS_IDENTIFIED | NS_RECOGNIZED))) { + na->last_seen = time(NULL); + if (na->last_quit) + free(na->last_quit); + na->last_quit = *av[0] ? sstrdup(av[0]) : NULL; + } +#ifndef STREAMLINED + if (LimitSessions) + del_session(user->host); +#endif + delete_user(user); +} + +/*************************************************************************/ + +/* Handle a KILL command. + * av[0] = nick being killed + * av[1] = reason + */ + +void do_kill(const char *source, int ac, char **av) +{ + User *user; + NickAlias *na; + + user = finduser(av[0]); + if (!user) + return; + if (debug) + alog("debug: %s killed", av[0]); + if ((na = user->na) && (!(na->status & NS_VERBOTEN)) + && (na->status & (NS_IDENTIFIED | NS_RECOGNIZED))) { + na->last_seen = time(NULL); + if (na->last_quit) + free(na->last_quit); + na->last_quit = *av[1] ? sstrdup(av[1]) : NULL; + + } +#ifndef STREAMLINED + if (LimitSessions) + del_session(user->host); +#endif + delete_user(user); +} + +/*************************************************************************/ +/*************************************************************************/ + +#if defined(IRC_ULTIMATE) || defined(IRC_ULTIMATE3) + +/* Is the given user protected from kicks and negative mode changes? */ + +int is_protected(User * user) +{ + return (user->mode & UMODE_p); +} +#endif + +/*************************************************************************/ + +/* Is the given nick an oper? */ + +int is_oper(User * user) +{ + return (user->mode & UMODE_o); +} + +/*************************************************************************/ +/*************************************************************************/ + +#if defined (IRC_ULTIMATE) || defined(IRC_ULTIMATE3) || defined(IRC_UNREAL) || defined(IRC_VIAGRA) || defined(IRC_HYBRID) +/* Is the given user ban-excepted? */ +int is_excepted(ChannelInfo * ci, User * user) +{ + int count, i; + int isexcepted = 0; + char **excepts; + + if (!ci->c) + return 0; + + count = ci->c->exceptcount; + excepts = scalloc(sizeof(char *) * count, 1); + memcpy(excepts, ci->c->excepts, sizeof(char *) * count); + + for (i = 0; i < count; i++) { + if (match_usermask(excepts[i], user)) { + isexcepted = 1; + } + } + free(excepts); + return isexcepted; +} + +/*************************************************************************/ + +/* Is the given MASK ban-excepted? */ +int is_excepted_mask(ChannelInfo * ci, char *mask) +{ + int count, i; + int isexcepted = 0; + char **excepts; + + if (!ci->c) + return 0; + + count = ci->c->exceptcount; + excepts = scalloc(sizeof(char *) * count, 1); + memcpy(excepts, ci->c->excepts, sizeof(char *) * count); + + for (i = 0; i < count; i++) { + if (match_wild_nocase(excepts[i], mask)) { + isexcepted = 1; + } + } + free(excepts); + return isexcepted; +} + +#endif +/*************************************************************************/ + +/* Does the user's usermask match the given mask (either nick!user@host or + * just user@host)? + */ + +int match_usermask(const char *mask, User * user) +{ + char *mask2 = sstrdup(mask); + char *nick, *username, *host; + int result; + + if (strchr(mask2, '!')) { + nick = strtok(mask2, "!"); + username = strtok(NULL, "@"); + } else { + nick = NULL; + username = strtok(mask2, "@"); + } + host = strtok(NULL, ""); + if (!username || !host) { + free(mask2); + return 0; + } + + if (nick) { + result = match_wild_nocase(nick, user->nick) + && match_wild_nocase(username, user->username) + && (match_wild_nocase(host, user->host) +#ifdef HAS_VHOST + || match_wild_nocase(host, user->vhost) +#endif + ); + } else { + result = match_wild_nocase(username, user->username) + && (match_wild_nocase(host, user->host) +#ifdef HAS_VHOST + || match_wild_nocase(host, user->vhost) +#endif + ); + } + + free(mask2); + return result; +} + +/*************************************************************************/ + +/* Split a usermask up into its constitutent parts. Returned strings are + * malloc()'d, and should be free()'d when done with. Returns "*" for + * missing parts. + */ + +void split_usermask(const char *mask, char **nick, char **user, + char **host) +{ + char *mask2 = sstrdup(mask); + + *nick = strtok(mask2, "!"); + *user = strtok(NULL, "@"); + *host = strtok(NULL, ""); + /* Handle special case: mask == user@host */ + if (*nick && !*user && strchr(*nick, '@')) { + *nick = NULL; + *user = strtok(mask2, "@"); + *host = strtok(NULL, ""); + } + if (!*nick) + *nick = "*"; + if (!*user) + *user = "*"; + if (!*host) + *host = "*"; + *nick = sstrdup(*nick); + *user = sstrdup(*user); + *host = sstrdup(*host); + free(mask2); +} + +/*************************************************************************/ + +/* Given a user, return a mask that will most likely match any address the + * user will have from that location. For IP addresses, wildcards the + * appropriate subnet mask (e.g. 35.1.1.1 -> 35.*; 128.2.1.1 -> 128.2.*); + * for named addresses, wildcards the leftmost part of the name unless the + * name only contains two parts. If the username begins with a ~, delete + * it. The returned character string is malloc'd and should be free'd + * when done with. + */ + +char *create_mask(User * u) +{ + char *mask, *s, *end; + int ulen = strlen(GetIdent(u)); + + /* Get us a buffer the size of the username plus hostname. The result + * will never be longer than this (and will often be shorter), thus we + * can use strcpy() and sprintf() safely. + */ + end = mask = smalloc(ulen + strlen(GetHost(u)) + 3); + end += sprintf(end, "%s%s@", + (ulen < + (*(GetIdent(u)) == + '~' ? USERMAX + 1 : USERMAX) ? "*" : ""), + (*(GetIdent(u)) == + '~' ? GetIdent(u) + 1 : GetIdent(u))); + + if (strspn(GetHost(u), "0123456789.") == strlen(GetHost(u)) + && (s = strchr(GetHost(u), '.')) + && (s = strchr(s + 1, '.')) + && (s = strchr(s + 1, '.')) + && (!strchr(s + 1, '.'))) { /* IP addr */ + s = sstrdup(GetHost(u)); + *strrchr(s, '.') = 0; + + sprintf(end, "%s.*", s); + free(s); + } else { + if ((s = strchr(GetHost(u), '.')) && strchr(s + 1, '.')) { + s = sstrdup(strchr(GetHost(u), '.') - 1); + *s = '*'; + strcpy(end, s); + free(s); + } else { + strcpy(end, GetHost(u)); + } + } + return mask; +} + +/*************************************************************************/ |