X-Git-Url: https://jfr.im/git/irc/evilnet/x3.git/blobdiff_plain/d0f04f713ca0b689f745842fcc9e61d24610f11a..201321bcb57b7306199c6b1ae0aed5eec2d783ad:/src/tools.c diff --git a/src/tools.c b/src/tools.c index 3ff571b..ce3c936 100644 --- a/src/tools.c +++ b/src/tools.c @@ -5,7 +5,7 @@ * * x3 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -313,7 +313,7 @@ irc_pton(irc_in_addr_t *addr, unsigned char *bits, const char *input) addr->in6[cpos + jj] = 0; } } else if (dot) { - unsigned int ip4; + uint32_t ip4; pos = irc_pton_ip4(input, bits, &ip4); if (pos) { addr->in6[5] = htons(65535); @@ -357,8 +357,20 @@ static char irc_tolower[256]; #undef tolower #define tolower(X) irc_tolower[(unsigned char)(X)] +void +irc_strtolower(char *str) { + char *p; + for(p = str;*p;p++) { + *p = tolower(*p); + } +} + int irccasecmp(const char *stra, const char *strb) { + if (!stra) + return -1; + if (!strb) + return 1; while (*stra && (tolower(*stra) == tolower(*strb))) stra++, strb++; return tolower(*stra) - tolower(*strb); @@ -385,6 +397,14 @@ irccasestr(const char *haystack, const char *needle) { return NULL; } +char * +ircstrlower(char *str) { + size_t ii; + for (ii = 0; str[ii] != '\0'; ++ii) + str[ii] = tolower(str[ii]); + return str; +} + int split_line(char *line, int irc_colon, int argv_size, char *argv[]) { @@ -533,6 +553,8 @@ match_ircglob(const char *text, const char *glob) return 0; m = m_tmp; n = ++n_tmp; + if (!*n) + return 0; break; case '\\': m++; @@ -584,13 +606,152 @@ int is_overmask(char *mask) } int -user_matches_glob(struct userNode *user, const char *orig_glob, int flags) +user_matches_glob(struct userNode *user, const char *orig_glob, int flags, int shared) { - char *glob, *marker; + char *tmpglob, *glob, *marker; + char exttype = 0; + int extreverse = 0, is_extended = 0, match = 0, banned = 0; + unsigned int count, n; + struct modeNode *mn; + struct chanNode *channel; + struct banNode *ban; /* Make a writable copy of the glob */ glob = alloca(strlen(orig_glob)+1); strcpy(glob, orig_glob); + + /* Extended bans */ + tmpglob = alloca(strlen(orig_glob)+1); + tmpglob = strdup(orig_glob); + + if (*tmpglob == '~') { + tmpglob++; /* get rid of the ~ */ + + if (*tmpglob == '!') { + extreverse = 1; + tmpglob++; /* get rid of the ! */ + } + + exttype = *tmpglob; + tmpglob++; /* get rid of the type */ + + if (*tmpglob == ':') { + is_extended = 1; + tmpglob++; /* get rid of the : */ + glob = strdup(tmpglob); + } + } + + if (is_extended) { + log_module(MAIN_LOG, LOG_DEBUG, "Extended ban. T (%c) R (%d) M (%s)", exttype, extreverse, glob); + switch (exttype) { + case 'a': // account + if (user->handle_info) { + if (extreverse) { + if (0 != strcasecmp(glob, user->handle_info->handle)) + return 1; + } else { + if (0 == strcasecmp(glob, user->handle_info->handle)) + return 1; + } + } else { + if (extreverse) + return 1; + } + return match_ircglob(user->hostname, glob); + case 'c': // another channel + if (!strstr(glob, "#")) + return -1; + + if (extreverse) { + for (n=count=0; nchannels.used; n++) { + mn = user->channels.list[n]; + match = 0; + + if (*glob == '#') { + if (0 == strcasecmp(glob, mn->channel->name)) + match = 1; + } else { + if (0 == strcasecmp(glob+1, mn->channel->name)) { + if ((*glob == '@') && (mn->modes & MODE_CHANOP)) + match = 1; + else if ((*glob == '%') && (mn->modes & MODE_HALFOP)) + match = 1; + else if ((*glob == '+') && (mn->modes & MODE_VOICE)) + match = 1; + } + } + + if (match == 0) + banned = 1; + else { + banned = 0; + break; + } + } + } else { + for (n=count=0; nchannels.used; n++) { + mn = user->channels.list[n]; + match = 0; + + if (*glob == '#') { + if (0 == strcasecmp(glob, mn->channel->name)) + match = 1; + } else { + if (0 == strcasecmp(glob+1, mn->channel->name)) { + if ((*glob == '@') && (mn->modes & MODE_CHANOP)) + match = 1; + else if ((*glob == '%') && (mn->modes & MODE_HALFOP)) + match = 1; + else if ((*glob == '+') && (mn->modes & MODE_VOICE)) + match = 1; + } + } + + if (match == 1) + banned = 1; + } + } + + if (banned) + return 1; + else + return match_ircglob(user->hostname, glob); + case 'j': + if (shared == 0) { + if (*glob != '#') + return -1; + if ((channel = GetChannel(glob))) { + for (n = 0; n < channel->banlist.used; n++) { + ban = channel->banlist.list[n]; + if (user_matches_glob(user, ban->ban, flags, 1)) + return 1; + } + } + } + return match_ircglob(user->hostname, glob); + case 'n': /* this is handled ircd side */ + return match_ircglob(user->hostname, glob); + case 'q': /* this is handled ircd side */ + return match_ircglob(user->hostname, glob); + case 't': /* this is handled ircd side */ + return match_ircglob(user->hostname, glob); + case 'R': /* this is handled ircd side */ + return match_ircglob(user->hostname, glob); + case 'm': // mute by mark + if(user->mark && !strcmp(glob, user->mark)) + return true; + else + return false; + + case 'M': // mute by mark unless authed + return false; // can never match a logged in user + + default: + return -1; + } + } + /* Check the nick, if it's present */ if (flags & MATCH_USENICK) { if (!(marker = strchr(glob, '!'))) { @@ -625,9 +786,19 @@ user_matches_glob(struct userNode *user, const char *orig_glob, int flags) if (match_ircglob(hidden_host, glob)) return 1; } + + /* Match crypt hostname */ + if (match_ircglob(user->crypthost, glob)) + return 1; + + /* Match crypt IP */ + if (match_ircglob(user->cryptip, glob)) + return 1; + /* If only matching the visible hostnames, bail early. */ if ((flags & MATCH_VISIBLE) && IsHiddenHost(user) - && (IsFakeHost(user) || (hidden_host_suffix && user->handle_info))) + && (IsFakeHost(user) || (hidden_host_suffix && user->handle_info) || + user->crypthost || user->cryptip)) return 0; /* If it might be an IP glob, test that. */ if (!glob[strspn(glob, "0123456789./*?")] @@ -640,6 +811,27 @@ user_matches_glob(struct userNode *user, const char *orig_glob, int flags) int is_ircmask(const char *text) { + char *tmptext; + + if (*text == '~') { + tmptext = alloca(strlen(text)+1); + tmptext = strdup(text); + + tmptext++; /* get rid of the ~ */ + + if (*tmptext == '!') + tmptext++; /* get rid of the ! if it exists */ + + tmptext++; /* get rid of the ext ban type */ + + if (*tmptext == ':') { + tmptext++; /* get rid of the : */ + while (*tmptext && !isspace((char)*tmptext)) + tmptext++; /* get rid of the rest */ + return !*tmptext; + } + } + while (*text && (isalnum((char)*text) || strchr("-_[]|\\`^{}?*", *text))) text++; if (*text++ != '!') @@ -825,12 +1017,14 @@ ParseInterval(const char *interval) /* process the string, resetting the count if we find a unit character */ while ((c = *interval++)) { - if (isdigit((int)c)) { - partial = partial*10 + c - '0'; - } else { - seconds += TypeLength(c) * partial; - partial = 0; - } + if (isdigit((int)c)) { + partial = partial*10 + c - '0'; + } else if (strchr("yMwdhms", c)) { + seconds += TypeLength(c) * partial; + partial = 0; + } else { + return 0; + } } /* assume the last chunk is seconds (the normal case) */ return seconds + partial; @@ -1102,6 +1296,23 @@ char *mysep(char **sepstr, char *delim) return(retstr); } +/* Mallocing snprintf * + * + * If it overruns size, it will simply be safely truncated. + */ +char * +x3_msnprintf(const int size, const char *format, ...) +{ + va_list ap; + char* buff = calloc(sizeof(char *), size+1); + + va_start(ap, format); + vsnprintf(buff, size, format, ap); + va_end(ap); + buff = realloc(buff, strlen(buff) + 1); + return buff; +} + char *time2str(time_t thetime) { char *buf, *tmp; @@ -1158,3 +1369,125 @@ int valid_email(const char *email) return true; } +/* + * Create a string of form "foo!bar@fubar" given foo, bar and fubar + * as the parameters. If NULL, they become "*". + */ +#define NUH_BUFSIZE (NICKLEN + USERLEN + HOSTLEN + 10) +static char *make_nick_user_host(char *namebuf, const char *nick, + const char *name, const char *host) +{ + snprintf(namebuf, NUH_BUFSIZE, "%s!%s@%s", nick, name, host); + return namebuf; +} + +/* + * pretty_mask + * + * by Carlo Wood (Run), 05 Oct 1998. + * + * Canonify a mask. + * + * When the nick is longer then NICKLEN, it is cut off (its an error of course). + * When the user name or host name are too long (USERLEN and HOSTLEN + * respectively) then they are cut off at the start with a '*'. + * + * The following transformations are made: + * + * 1) xxx -> nick!*@* + * 2) xxx.xxx -> *!*@host + * 3) xxx!yyy -> nick!user@* + * 4) xxx@yyy -> *!user@host + * 5) xxx!yyy@zzz -> nick!user@host + */ +char *pretty_mask(char *mask) +{ + static char star[2] = { '*', 0 }; + static char retmask[NUH_BUFSIZE] = ""; + char *last_dot = NULL; + char *ptr = NULL; + + /* Case 1: default */ + char *nick = mask; + char *user = star; + char *host = star; + + /* Do a _single_ pass through the characters of the mask: */ + for (ptr = mask; *ptr; ++ptr) + { + if (*ptr == '!') + { + /* Case 3 or 5: Found first '!' (without finding a '@' yet) */ + user = ++ptr; + host = star; + } + else if (*ptr == '@') + { + /* Case 4: Found last '@' (without finding a '!' yet) */ + nick = star; + user = mask; + host = ++ptr; + } + else if (*ptr == '.') + { + /* Case 2: Found last '.' (without finding a '!' or '@' yet) */ + last_dot = ptr; + continue; + } + else + continue; + for (; *ptr; ++ptr) + { + if (*ptr == '@') + { + /* Case 4 or 5: Found last '@' */ + host = ptr + 1; + } + } + break; + } + if (user == star && last_dot) + { + /* Case 2: */ + nick = star; + user = star; + host = mask; + } + /* Check lengths */ + if (nick != star) + { + char *nick_end = (user != star) ? user - 1 : ptr; + if (nick_end - nick > NICKLEN) + nick[NICKLEN] = 0; + *nick_end = 0; + } + if (user != star) + { + char *user_end = (host != star) ? host - 1 : ptr; + if (user_end - user > USERLEN) + { + user = user_end - USERLEN; + *user = '*'; + } + *user_end = 0; + } + if (host != star && ptr - host > HOSTLEN) + { + host = ptr - HOSTLEN; + *host = '*'; + } + return make_nick_user_host(retmask, nick, user, host); +} + +int str_is_number(const char *str) +{ + char *ptr; + int ret = false; + for(ptr = (char *)str;*ptr;ptr++) { + if((*ptr >= '0' && *ptr <= '9') || *ptr == '-') + ret = true; + else + return false; + } + return ret; +}