fix possible crash on user deletion
[srvx.git] / src / tools.c
index 4b852615c8bef349086e2c852e4cc557cdba5997..b10bc7be4379b5b573008142c58bb539ffe9c9c3 100644 (file)
@@ -18,6 +18,7 @@
  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
  */
 
+#include "helpfile.h"
 #include "log.h"
 #include "nickserv.h"
 #include "recdb.h"
@@ -49,13 +50,23 @@ static const unsigned char convert2n[256] = {
    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,
-  52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0, 
+  52,53,54,55,56,57,58,59,60,61, 0, 0, 0, 0, 0, 0,
    0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
   15,16,17,18,19,20,21,22,23,24,25,62, 0,63, 0, 0,
    0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
   41,42,43,44,45,46,47,48,49,50,51, 0, 0, 0, 0, 0
 };
 
+static const unsigned char ctype[256] = {
+   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, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
+   0,10,11,12,13,14,15, 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,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
 unsigned long int
 base64toint(const char* s, int count)
 {
@@ -77,6 +88,273 @@ const char* inttobase64(char* buf, unsigned int v, unsigned int count)
   return buf;
 }
 
+unsigned int
+irc_ntop(char *output, unsigned int out_size, const irc_in_addr_t *addr)
+{
+    static const char hexdigits[] = "0123456789abcdef";
+    unsigned int pos;
+
+    assert(output);
+    assert(addr);
+
+    if (irc_in_addr_is_ipv4(*addr)) {
+        unsigned int ip4;
+
+        ip4 = (ntohs(addr->in6[6]) << 16) | ntohs(addr->in6[7]);
+        pos = snprintf(output, out_size, "%u.%u.%u.%u", (ip4 >> 24), (ip4 >> 16) & 255, (ip4 >> 8) & 255, ip4 & 255);
+   } else {
+        unsigned int part, max_start, max_zeros, curr_zeros, ii;
+
+        /* Find longest run of zeros. */
+        for (max_start = max_zeros = curr_zeros = ii = 0; ii < 8; ++ii) {
+            if (!addr->in6[ii])
+                curr_zeros++;
+            else if (curr_zeros > max_zeros) {
+                max_start = ii - curr_zeros;
+                max_zeros = curr_zeros;
+                curr_zeros = 0;
+            }
+        }
+        if (curr_zeros > max_zeros) {
+            max_start = ii - curr_zeros;
+            max_zeros = curr_zeros;
+        }
+
+        /* Print out address. */
+#define APPEND(CH) do { if (pos < out_size) output[pos] = (CH); pos++; } while (0)
+        for (pos = 0, ii = 0; ii < 8; ++ii) {
+            if ((max_zeros > 0) && (ii == max_start)) {
+                if (ii == 0)
+                    APPEND(':');
+                APPEND(':');
+                ii += max_zeros - 1;
+                continue;
+            }
+            part = ntohs(addr->in6[ii]);
+            if (part >= 0x1000)
+                APPEND(hexdigits[part >> 12]);
+            if (part >= 0x100)
+                APPEND(hexdigits[(part >> 8) & 15]);
+            if (part >= 0x10)
+                APPEND(hexdigits[(part >> 4) & 15]);
+            APPEND(hexdigits[part & 15]);
+            if (ii < 7)
+                APPEND(':');
+        }
+#undef APPEND
+        output[pos < out_size ? pos : out_size - 1] = '\0';
+    }
+
+    return pos;
+}
+
+unsigned int
+irc_ntop_mask(char *output, unsigned int out_size, const irc_in_addr_t *addr, unsigned char bits)
+{
+    char base_addr[IRC_NTOP_MAX_SIZE];
+    int len;
+
+    if (bits >= 128)
+        return irc_ntop(output, out_size, addr);
+    if (!irc_ntop(base_addr, sizeof(base_addr), addr))
+        return 0;
+    len = snprintf(output, out_size, "%s/%d", base_addr, bits);
+    if ((unsigned int)len >= out_size)
+        return 0;
+    return len;
+}
+
+static unsigned int
+irc_pton_ip4(const char *input, unsigned char *pbits, uint32_t *output)
+{
+    unsigned int dots = 0, pos = 0, part = 0, ip = 0, bits = 32;
+
+    /* Intentionally no support for bizarre IPv4 formats (plain
+     * integers, octal or hex components) -- only vanilla dotted
+     * decimal quads, optionally with trailing /nn.
+     */
+    if (input[0] == '.')
+        return 0;
+    while (1) switch (input[pos]) {
+    default:
+        if (dots < 3)
+            return 0;
+    out:
+        ip |= part << (24 - 8 * dots++);
+        *output = htonl(ip);
+        if (pbits)
+            *pbits = bits;
+        return pos;
+    case '.':
+        if (input[++pos] == '.')
+            return 0;
+        ip |= part << (24 - 8 * dots++);
+        part = 0;
+        if (input[pos] == '*') {
+            while (input[++pos] == '*') ;
+            if (input[pos] != '\0')
+                return 0;
+            if (pbits)
+                *pbits = dots * 8;
+            *output = htonl(ip);
+            return pos;
+        }
+        break;
+    case '/':
+        if (!pbits || !isdigit(input[pos + 1]))
+            return 0;
+        for (bits = 0; isdigit(input[++pos]); )
+            bits = bits * 10 + input[pos] - '0';
+        if (bits > 32)
+            return 0;
+        goto out;
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+        part = part * 10 + input[pos++] - '0';
+        if (part > 255)
+            return 0;
+        break;
+    }
+}
+
+unsigned int
+irc_pton(irc_in_addr_t *addr, unsigned char *bits, const char *input)
+{
+    const char *part_start = NULL;
+    char *colon;
+    char *dot;
+    unsigned int part = 0, pos = 0, ii = 0, cpos = 8;
+
+    assert(input);
+    memset(addr, 0, sizeof(*addr));
+    colon = strchr(input, ':');
+    dot = strchr(input, '.');
+
+    if (colon && (!dot || (dot > colon))) {
+        /* Parse IPv6, possibly like ::127.0.0.1.
+         * This is pretty straightforward; the only trick is borrowed
+         * from Paul Vixie (BIND): when it sees a "::" continue as if
+         * it were a single ":", but note where it happened, and fill
+         * with zeros afterwards.
+         */
+        if (input[pos] == ':') {
+            if ((input[pos+1] != ':') || (input[pos+2] == ':'))
+                return 0;
+            cpos = 0;
+            pos += 2;
+            part_start = input + pos;
+        }
+        while (ii < 8) switch (input[pos]) {
+        case '0': case '1': case '2': case '3': case '4':
+        case '5': case '6': case '7': case '8': case '9':
+        case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+        case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+            part = (part << 4) | (ctype[(unsigned char)input[pos++]] & 15);
+            if (part > 0xffff)
+                return 0;
+            break;
+        case ':':
+            part_start = input + ++pos;
+            if (input[pos] == '.')
+                return 0;
+            addr->in6[ii++] = htons(part);
+            part = 0;
+            if (input[pos] == ':') {
+                if (cpos < 8)
+                    return 0;
+                cpos = ii;
+            }
+            break;
+        case '.': {
+            uint32_t ip4;
+            unsigned int len;
+            len = irc_pton_ip4(part_start, bits, &ip4);
+            if (!len || (ii > 6))
+                return 0;
+            memcpy(addr->in6 + ii, &ip4, sizeof(ip4));
+            if (bits)
+                *bits += 96;
+            ii += 2;
+            pos = part_start + len - input;
+            goto finish;
+        }
+        case '/':
+            if (!bits || !isdigit(input[pos + 1]))
+                return 0;
+            addr->in6[ii++] = htons(part);
+            for (part = 0; isdigit(input[++pos]); )
+                part = part * 10 + input[pos] - '0';
+            if (part > 128)
+                return 0;
+            *bits = part;
+            goto finish;
+        case '*':
+            while (input[++pos] == '*') ;
+            if (input[pos] != '\0' || cpos < 8)
+                return 0;
+            if (bits)
+                *bits = ii * 16;
+            return pos;
+        default:
+            addr->in6[ii++] = htons(part);
+            if (cpos == 8 && ii < 8)
+                return 0;
+            if (bits)
+                *bits = 128;
+            goto finish;
+        }
+    finish:
+        /* Shift stuff after "::" up and fill middle with zeros. */
+        if (cpos < 8) {
+            unsigned int jj;
+            for (jj = 0; jj < ii - cpos; jj++)
+                addr->in6[7 - jj] = addr->in6[ii - jj - 1];
+            for (jj = 0; jj < 8 - ii; jj++)
+                addr->in6[cpos + jj] = 0;
+        }
+    } else if (dot) {
+        uint32_t ip4;
+        pos = irc_pton_ip4(input, bits, &ip4);
+        if (pos) {
+/* glibc's htons() macro is not -Wshadow-safe. */
+#undef htons
+            addr->in6[5] = htons(65535);
+            addr->in6[6] = htons(ntohl(ip4) >> 16);
+            addr->in6[7] = htons(ntohl(ip4) & 65535);
+            if (bits)
+                *bits += 96;
+        }
+    } else if (input[0] == '*') {
+        while (input[++pos] == '*') ;
+        if (input[pos] != '\0')
+            return 0;
+        if (bits)
+            *bits = 0;
+    }
+    return pos;
+}
+
+const char *irc_ntoa(const irc_in_addr_t *addr)
+{
+    static char ntoa[IRC_NTOP_MAX_SIZE];
+    irc_ntop(ntoa, sizeof(ntoa), addr);
+    return ntoa;
+}
+
+unsigned int
+irc_check_mask(const irc_in_addr_t *check, const irc_in_addr_t *mask, unsigned char bits)
+{
+    unsigned int ii;
+
+    for (ii = 0; (ii < 8) && (bits > 16); bits -= 16, ++ii)
+        if (check->in6[ii] != mask->in6[ii])
+            return 0;
+    if (ii < 8 && bits > 0
+        && (ntohs(check->in6[ii] ^ mask->in6[ii]) >> (16 - bits)))
+        return 0;
+    return 1;
+}
+
 static char irc_tolower[256];
 #undef tolower
 #define tolower(X) irc_tolower[(unsigned char)(X)]
@@ -109,31 +387,40 @@ 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[])
 {
     int argc = 0;
     int n;
     while (*line && (argc < argv_size)) {
-       while (*line == ' ') *line++ = 0;
-       if (*line == ':' && irc_colon && argc > 0) {
-           /* the rest is a single parameter */
-           argv[argc++] = line + 1;
-           break;
-       }
+        while (*line == ' ')
+            *line++ = 0;
+        if (*line == ':' && irc_colon && argc > 0) {
+            /* the rest is a single parameter */
+            argv[argc++] = line + 1;
+            break;
+        }
         if (!*line)
             break;
-       argv[argc++] = line;
-       if (argc >= argv_size)
+        argv[argc++] = line;
+        if (argc >= argv_size)
             break;
-       while (*line != ' ' && *line) line++;
+        while (*line != ' ' && *line)
+            line++;
     }
 #ifdef NDEBUG
     n = 0;
 #else
-    for (n=argc; n<argv_size; n++) {
+    for (n=argc; n<argv_size; n++)
         argv[n] = (char*)0xFEEDBEEF;
-    }
 #endif
     return argc;
 }
@@ -153,7 +440,7 @@ int mmatch(const char *old_mask, const char *new_mask)
     if (*m == '*')
     {
       while (*m == '*')
-       m++;
+        m++;
       wild = 1;
       ma = m;
       na = n;
@@ -162,25 +449,25 @@ int mmatch(const char *old_mask, const char *new_mask)
     if (!*m)
     {
       if (!*n)
-       return 0;
+        return 0;
       for (m--; (m > old_mask) && (*m == '?'); m--)
-       ;
+        ;
       if ((*m == '*') && (m > old_mask) && (m[-1] != '\\'))
-       return 0;
+        return 0;
       if (!wild)
-       return 1;
+        return 1;
       m = ma;
 
       /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
       if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
-       ++na;
+        ++na;
 
       n = ++na;
     }
     else if (!*n)
     {
       while (*m == '*')
-       m++;
+        m++;
       return (*m != 0);
     }
     if ((*m == '\\') && ((m[1] == '*') || (m[1] == '?')))
@@ -217,23 +504,23 @@ int mmatch(const char *old_mask, const char *new_mask)
  *  cases upfront (which took 2 hours!)).
  */
     if ((*m == '*' && !mq) ||
-       ((!mq || nq) && tolower(*m) == tolower(*n)) ||
-       (*m == '?' && !mq && (*n != '*' || nq)))
+        ((!mq || nq) && tolower(*m) == tolower(*n)) ||
+        (*m == '?' && !mq && (*n != '*' || nq)))
     {
       if (*m)
-       m++;
+        m++;
       if (*n)
-       n++;
+        n++;
     }
     else
     {
       if (!wild)
-       return 1;
+        return 1;
       m = ma;
 
       /* Added to `mmatch' : Because '\?' and '\*' now is one character: */
       if ((*na == '\\') && ((na[1] == '*') || (na[1] == '?')))
-       ++na;
+        ++na;
 
       n = ++na;
     }
@@ -243,80 +530,78 @@ int mmatch(const char *old_mask, const char *new_mask)
 int
 match_ircglob(const char *text, const char *glob)
 {
-    unsigned int star_p, q_cnt;
-    while (1) {
-       switch (*glob) {
-       case 0:
-           return !*text;
-        case '\\':
-            glob++;
-            /* intentionally not tolower(...) so people can force
-             * capitalization, or we can overload \ in the future */
-            if (*text++ != *glob++)
-                return 0;
-            break;
-       case '*':
-        case '?':
-            star_p = q_cnt = 0;
-            do {
-                if (*glob == '*')
-                    star_p = 1;
-                else if (*glob == '?')
-                    q_cnt++;
-                else
-                    break;
-                glob++;
-            } while (1);
-            while (q_cnt) {
-                if (!*text++)
-                    return 0;
-                q_cnt--;
-            }
-            if (star_p) {
-                /* if this is the last glob character, it will match any text */
-                if (!*glob)
-                    return 1;
-                /* Thanks to the loop above, we know that the next
-                 * character is a normal character.  So just look for
-                 * the right character.
-                 */
-                for (; *text; text++) {
-                    if ((tolower(*text) == tolower(*glob))
-                        && match_ircglob(text+1, glob+1)) {
-                        return 1;
-                    }
-                }
-                return 0;
-            }
-            /* if !star_p, fall through to normal character case,
-             * first checking to see if ?s carried us to the end */
-            if (!*glob && !*text)
+    const char *m = glob, *n = text;
+    const char *m_tmp = glob, *n_tmp = text;
+    int star_p;
+
+    for (;;) switch (*m) {
+    case '\0':
+        if (!*n)
+            return 1;
+    backtrack:
+        if (m_tmp == glob)
+            return 0;
+        m = m_tmp;
+        n = ++n_tmp;
+        if (!*n)
+            return 0;
+        break;
+    case '\\':
+        m++;
+        /* allow escaping to force capitalization */
+        if (*m++ != *n++)
+            goto backtrack;
+        break;
+    case '*': case '?':
+        for (star_p = 0; ; m++) {
+            if (*m == '*')
+                star_p = 1;
+            else if (*m == '?') {
+                if (!*n++)
+                    goto backtrack;
+            } else break;
+        }
+        if (star_p) {
+            if (!*m)
                 return 1;
-       default:
-           if (!*text)
-                return 0;
-           while (*text && *glob && *glob != '*' && *glob != '?' && *glob != '\\') {
-               if (tolower(*text++) != tolower(*glob++))
+            else if (*m == '\\') {
+                m_tmp = ++m;
+                if (!*m)
                     return 0;
-           }
-       }
+                for (n_tmp = n; *n && *n != *m; n++) ;
+            } else {
+                m_tmp = m;
+                for (n_tmp = n; *n && tolower(*n) != tolower(*m); n++) ;
+            }
+        }
+        /* and fall through */
+    default:
+        if (!*n)
+            return *m == '\0';
+        if (tolower(*m) != tolower(*n))
+            goto backtrack;
+        m++;
+        n++;
+        break;
     }
 }
 
 extern const char *hidden_host_suffix;
 
 int
-user_matches_glob(struct userNode *user, const char *orig_glob, int include_nick)
+user_matches_glob(struct userNode *user, const char *orig_glob, int flags)
 {
+    irc_in_addr_t mask;
     char *glob, *marker;
+    unsigned char mask_bits;
 
     /* Make a writable copy of the glob */
     glob = alloca(strlen(orig_glob)+1);
     strcpy(glob, orig_glob);
     /* Check the nick, if it's present */
-    if (include_nick) {
+    if (flags & MATCH_USENICK) {
         if (!(marker = strchr(glob, '!'))) {
-            log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include a '!'", user->nick, orig_glob, include_nick);
+            log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include a '!'", user->nick, orig_glob, flags);
             return 0;
         }
         *marker = 0;
@@ -325,27 +610,34 @@ user_matches_glob(struct userNode *user, const char *orig_glob, int include_nick
     }
     /* Check the ident */
     if (!(marker = strchr(glob, '@'))) {
-        log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include an '@'", user->nick, orig_glob, include_nick);
+        log_module(MAIN_LOG, LOG_ERROR, "user_matches_glob(\"%s\", \"%s\", %d) called, and glob doesn't include an '@'", user->nick, orig_glob, flags);
         return 0;
     }
     *marker = 0;
-    if (!match_ircglob(user->ident, glob))
+    if (((IsFakeIdent(user) && IsHiddenHost(user) && (flags & MATCH_VISIBLE)) || !match_ircglob(user->ident, glob)) &&
+        !(IsFakeIdent(user) && match_ircglob(user->fakeident, glob)))
         return 0;
     glob = marker + 1;
-    /* Now check the host part */
-    if (isdigit(*glob) && !glob[strspn(glob, "0123456789./*?")]) {
-        /* Looks like an IP-based mask */
-        return match_ircglob(inet_ntoa(user->ip), glob);
-    } else {
-        /* The host part of the mask isn't IP-based */
-        if (hidden_host_suffix && user->handle_info) {
-            char hidden_host[HOSTLEN+1];
-            snprintf(hidden_host, sizeof(hidden_host), "%s.%s", user->handle_info->handle, hidden_host_suffix);
-            if (match_ircglob(hidden_host, glob))
-                return 1;
-        }
-        return match_ircglob(user->hostname, glob);
+    /* Check for a fakehost match. */
+    if (IsFakeHost(user) && match_ircglob(user->fakehost, glob))
+        return 1;
+    /* Check for an account match. */
+    if (hidden_host_suffix && user->handle_info) {
+        char hidden_host[HOSTLEN+1];
+        snprintf(hidden_host, sizeof(hidden_host), "%s.%s", user->handle_info->handle, hidden_host_suffix);
+        if (match_ircglob(hidden_host, 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)))
+        return 0;
+    /* If it might be an IP glob, test that. */
+    if (irc_pton(&mask, &mask_bits, glob)
+        && irc_check_mask(&user->ip, &mask, mask_bits))
+        return 1;
+    /* None of the above; could only be a hostname match. */
+    return match_ircglob(user->hostname, glob);
 }
 
 int
@@ -374,7 +666,7 @@ is_gline(const char *text)
         return 0;
     if (!*text)
         return 0;
-    while (*text && (isalnum((char)*text) || strchr(".-?*", *text)))
+    while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
         text++;
     return !*text;
 }
@@ -401,9 +693,9 @@ split_ircmask(char *text, char **nick, char **ident, char **host)
     *text = 0;
     if (ident)
         *ident = start;
-    
+
     start = ++text;
-    while (*text && (isalnum((char)*text) || strchr(".-?*", *text)))
+    while (*text && (isalnum((char)*text) || strchr(".-?*:", *text)))
         text++;
     if (host)
         *host = start;
@@ -426,20 +718,20 @@ sanitize_ircmask(char *input)
     mask = input;
     while(*input++ != '!')
     {
-       length++;
+        length++;
     }
     if(length > NICKLEN)
     {
-       mask += NICKLEN;
-       *mask++ = '!';
+        mask += NICKLEN;
+        *mask++ = '!';
 
-       /* This flag is used to indicate following parts should
-          be shifted. */
-       flag = 1;
+        /* This flag is used to indicate following parts should
+           be shifted. */
+        flag = 1;
     }
     else
     {
-       mask = input;
+        mask = input;
     }
 
     /* The ident and host must be truncated at the beginning and
@@ -448,46 +740,46 @@ sanitize_ircmask(char *input)
     start = input;
     while(*input++ != '@')
     {
-       length++;
+        length++;
     }
     if(length > USERLEN || flag)
     {
-       if(length > USERLEN)
-       {
-           start = input - USERLEN;
-           *mask++ = '*';
-       }
-       while(*start != '@')
-       {
-           *mask++ = *start++;
-       }
-       *mask++ = '@';
-
-       flag = 1;
+        if(length > USERLEN)
+        {
+            start = input - USERLEN;
+            *mask++ = '*';
+        }
+        while(*start != '@')
+        {
+            *mask++ = *start++;
+        }
+        *mask++ = '@';
+
+        flag = 1;
     }
     else
     {
-       mask = input;
+        mask = input;
     }
 
     length = 0;
     start = input;
     while(*input++)
     {
-       length++;
+        length++;
     }
     if(length > HOSTLEN || flag)
     {
-       if(length > HOSTLEN)
-       {
-           start = input - HOSTLEN;
-           *mask++ = '*';
-       }
-       while(*start)
-       {
-           *mask++ = *start++;
-       }
-       *mask = '\0';
+        if(length > HOSTLEN)
+        {
+            start = input - HOSTLEN;
+            *mask++ = '*';
+        }
+        while(*start)
+        {
+            *mask++ = *start++;
+        }
+        *mask = '\0';
     }
 
     return output;
@@ -517,12 +809,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;
@@ -556,74 +850,6 @@ ParseVolume(const char *volume)
     return accum + partial;
 }
 
-int
-parse_ipmask(const char *str, struct in_addr *addr, unsigned long *mask)
-{
-    int accum, pos;
-    unsigned long t_a, t_m;
-
-    t_a = t_m = pos = 0;
-    if (addr)
-        addr->s_addr = htonl(t_a);
-    if (mask)
-        *mask = t_m;
-    while (*str) {
-        if (!isdigit(*str))
-            return 0;
-        accum = 0;
-        do {
-            accum = (accum * 10) + *str++ - '0';
-        } while (isdigit(*str));
-        if (accum > 255)
-            return 0;
-        t_a = (t_a << 8) | accum;
-        t_m = (t_m << 8) | 255;
-        pos += 8;
-        if (*str == '.') {
-            str++;
-            while (*str == '*') {
-                str++;
-                if (*str == '.') {
-                    t_a <<= 8;
-                    t_m <<= 8;
-                    pos += 8;
-                    str++;
-                } else if (*str == 0) {
-                    t_a <<= 32 - pos;
-                    t_m <<= 32 - pos;
-                    pos = 32;
-                    goto out;
-                } else
-                    return 0;
-            }
-        } else if (*str == '/') {
-            int start = pos;
-            accum = 0;
-            do {
-                accum = (accum * 10) + *str++ - '0';
-            } while (isdigit(*str));
-            while (pos < start+accum && pos < 32) {
-                t_a = (t_a << 1) | 0;
-                t_m = (t_m << 1) | 1;
-                pos++;
-            }
-            if (pos != start+accum)
-                return 0;
-        } else if (*str == 0)
-            break;
-        else
-            return 0;
-    }
-out:
-    if (pos != 32)
-        return 0;
-    if (addr)
-        addr->s_addr = htonl(t_a);
-    if (mask)
-        *mask = t_m;
-    return 1;
-}
-
 char *
 unsplit_string(char *set[], unsigned int max, char *dest)
 {
@@ -642,44 +868,48 @@ unsplit_string(char *set[], unsigned int max, char *dest)
 }
 
 char *
-intervalString2(char *output, time_t interval, int brief)
+intervalString(char *output, unsigned long interval, struct handle_info *hi)
 {
     static const struct {
-        const char *name;
-        long length;
+        const char *msg_single;
+        const char *msg_plural;
+        unsigned long length;
     } unit[] = {
-        { "year", 365 * 24 * 60 * 60 },
-        { "week",   7 * 24 * 60 * 60 },
-        { "day",        24 * 60 * 60 },
-        { "hour",            60 * 60 },
-        { "minute",               60 },
-        { "second",                1 }
+        { "MSG_YEAR",   "MSG_YEARS", 365 * 24 * 60 * 60 },
+        { "MSG_WEEK",   "MSG_WEEKS",   7 * 24 * 60 * 60 },
+        { "MSG_DAY",    "MSG_DAYS",        24 * 60 * 60 },
+        { "MSG_HOUR",   "MSG_HOURS",            60 * 60 },
+        { "MSG_MINUTE", "MSG_MINUTES",               60 },
+        { "MSG_SECOND", "MSG_SECONDS",                1 }
     };
+    struct language *lang;
+    const char *msg;
     unsigned int type, words, pos, count;
 
+    lang = hi ? hi->language : lang_C;
     if(!interval)
     {
-       strcpy(output, brief ? "0s" : "0 seconds");
-       return output;
+        msg = language_find_message(lang, "MSG_0_SECONDS");
+        return strcpy(output, msg);
     }
 
     for (type = 0, words = pos = 0;
          interval && (words < 2) && (type < ArrayLength(unit));
          type++) {
-       if (interval < unit[type].length)
+        if (interval < unit[type].length)
             continue;
         count = interval / unit[type].length;
         interval = interval % unit[type].length;
 
-        if (brief)
-            pos += sprintf(output + pos, "%d%c", count, unit[type].name[0]);
-        else if (words == 1)
-            pos += sprintf(output + pos, " and %d %s", count, unit[type].name);
+        if (words++ == 1) {
+            msg = language_find_message(lang, "MSG_AND");
+            pos += sprintf(output + pos, " %s ", msg);
+        }
+        if (count == 1)
+            msg = language_find_message(lang, unit[type].msg_single);
         else
-            pos += sprintf(output + pos, "%d %s", count, unit[type].name);
-        if (count != 1)
-            output[pos++] = 's';
-        words++;
+            msg = language_find_message(lang, unit[type].msg_plural);
+        pos += sprintf(output + pos, "%d %s", count, msg);
     }
 
     output[pos] = 0;
@@ -738,7 +968,7 @@ string_buffer_append_vprintf(struct string_buffer *buf, const char *fmt, va_list
         /* pre-C99 behavior; double buffer size until it is big enough */
         va_end(working);
         VA_COPY(working, args);
-        while ((ret = vsnprintf(buf->list + buf->used, buf->size, fmt, working)) == -1) {
+        while ((ret = vsnprintf(buf->list + buf->used, buf->size - buf->used, fmt, working)) <= 0) {
             buf->size += len;
             buf->list = realloc(buf->list, buf->size);
             va_end(working);