Add IPv6 support.
[srvx.git] / src / proto-p10.c
index 8e5bc3ef4a8dfd07a38c9597a887b8f038315d66..cf9b07637dfb630ae2e3e333a53ec46b8984adfb 100644 (file)
@@ -374,13 +374,84 @@ irc_server(struct server *srv)
     }
 }
 
+static void
+irc_p10_pton(irc_in_addr_t *ip, const char *input)
+{
+    if (strlen(input) == 6) {
+        unsigned int value;
+        memset(ip, 0, 6 * sizeof(ip->in6[0]));
+        value = base64toint(input, 6);
+        ip->in6[6] = htons(value >> 16);
+        ip->in6[7] = htons(value & 65535);
+    } else {
+        unsigned int pos = 0;
+        do {
+            if (*input == '_') {
+                unsigned int left;
+                for (left = (25 - strlen(input)) / 3; left; left--)
+                    ip->in6[pos++] = 0;
+                input++;
+            } else {
+                ip->in6[pos++] = ntohs(base64toint(input, 3));
+                input += 3;
+            }
+        } while (pos < 8);
+    }
+}
+
+static void
+irc_p10_ntop(char *output, const irc_in_addr_t *ip)
+{
+    if (irc_in_addr_is_ipv4(*ip)) {
+        unsigned int in4;
+        in4 = (ntohs(ip->in6[6]) << 16) | ntohs(ip->in6[7]);
+        inttobase64(output, in4, 6);
+        output[6] = '\0';
+    } else if (irc_in_addr_is_ipv6(*ip)) {
+        unsigned int max_start, max_zeros, curr_zeros, zero, ii;
+        /* Can start by printing out the leading non-zero parts. */
+        for (ii = 0; (ip->in6[ii]) && (ii < 8); ++ii) {
+            inttobase64(output, ntohs(ip->in6[ii]), 3);
+            output += 3;
+        }
+        /* Find the longest run of zeros. */
+        for (max_start = zero = ii, max_zeros = curr_zeros = 0; ii < 8; ++ii) {
+            if (!ip->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;
+            curr_zeros = 0;
+        }
+        /* Print the rest of the address */
+        for (ii = zero; ii < 8; ) {
+            if ((ii == max_start) && max_zeros) {
+                *output++ = '_';
+                ii += max_zeros;
+            } else {
+                inttobase64(output, ntohs(ip->in6[ii]), 3);
+                output += 3;
+            }
+        }
+        *output = '\0';
+    } else {
+        strcpy(output, "???");
+    }
+}
+
 void
 irc_user(struct userNode *user)
 {
-    char b64ip[7];
+    char b64ip[25];
     if (!user)
         return;
-    inttobase64(b64ip, ntohl(user->ip.s_addr), 6);
+    irc_p10_ntop(b64ip, &user->ip);
     if (user->modes) {
         int modelen;
         char modes[32];
@@ -782,13 +853,11 @@ static CMD_FUNC(cmd_whois)
     }
     irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info);
     if (his_servername && his_servercomment)
-      irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, his_servername, his_servercomment);
+        irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, his_servername, his_servercomment);
     else
-    irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description);
-
-    if (IsOper(who)) {
+        irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description);
+    if (IsOper(who))
         irc_numeric(from, RPL_WHOISOPERATOR, "%s :is a megalomaniacal power hungry tyrant", who->nick);
-    }
     irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick);
     return 1;
 }
@@ -800,7 +869,7 @@ static CMD_FUNC(cmd_server)
 
     if (argc < 8)
         return 0;
-    if (origin) {
+    if (self->uplink) {
         /* another server introduced us */
         srv = AddServer(GetServerH(origin), argv[1], atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), argv[6], argv[argc-1]);
         if (!srv)
@@ -1078,7 +1147,10 @@ static CMD_FUNC(cmd_burst)
                     mode |= MODE_CHANOP;
                 else if (sep == 'v')
                     mode |= MODE_VOICE;
-                else
+                else if (isdigit(sep)) {
+                    mode |= MODE_CHANOP;
+                    while (isdigit(*end)) end++;
+                } else
                     break;
             }
             if (rel_age < 0)
@@ -1578,6 +1650,7 @@ init_parse(void)
     dict_insert(irc_func_dict, "401", cmd_dummy); /* target left network */
     dict_insert(irc_func_dict, "403", cmd_dummy); /* no such channel */
     dict_insert(irc_func_dict, "404", cmd_dummy); /* cannot send to channel */
+    dict_insert(irc_func_dict, "439", cmd_dummy); /* target change too fast */
     dict_insert(irc_func_dict, "441", cmd_dummy); /* target isn't on that channel */
     dict_insert(irc_func_dict, "442", cmd_dummy); /* you aren't on that channel */
     dict_insert(irc_func_dict, "443", cmd_dummy); /* is already on channel (after invite?) */
@@ -1846,12 +1919,13 @@ AddClone(const char *nick, const char *ident, const char *hostname, const char *
 
 int
 is_valid_nick(const char *nick) {
+    unsigned int ii;
     /* IRC has some of The Most Fucked-Up ideas about character sets
      * in the world.. */
     if (!isalpha(*nick) && !strchr("{|}~[\\]^_`", *nick))
         return 0;
-    for (++nick; *nick; ++nick)
-        if (!isalnum(*nick) && !strchr("{|}~[\\]^-_`", *nick))
+    for (ii = 0; nick[ii]; ++ii)
+        if (!isalnum(nick[ii]) && !strchr("{|}~[\\]^-_`", nick[ii]))
             return 0;
     if (strlen(nick) > nicklen)
         return 0;
@@ -1909,7 +1983,7 @@ AddUser(struct server* uplink, const char *nick, const char *ident, const char *
     safestrncpy(uNode->info, userinfo, sizeof(uNode->info));
     safestrncpy(uNode->hostname, hostname, sizeof(uNode->hostname));
     safestrncpy(uNode->numeric, numeric, sizeof(uNode->numeric));
-    uNode->ip.s_addr = htonl(base64toint(realip, 6));
+    irc_p10_pton(&uNode->ip, realip);
     uNode->timestamp = timestamp;
     modeList_init(&uNode->channels);
     uNode->uplink = uplink;
@@ -1941,6 +2015,8 @@ DelUser(struct userNode* user, struct userNode *killer, int announce, const char
 {
     unsigned int n;
 
+    verify(user);
+
     /* mark them as dead, in case anybody cares */
     user->dead = 1;
 
@@ -1982,15 +2058,16 @@ DelUser(struct userNode* user, struct userNode *killer, int announce, const char
         free_user(user);
 }
 
+static void call_oper_funcs(struct userNode *user);
+
 void mod_usermode(struct userNode *user, const char *mode_change) {
-    static void call_oper_funcs(struct userNode *user);
     int add = 1;
     const char *word = mode_change;
 
     if (!user || !mode_change)
         return;
-    while (*word != ' ' && *word) word++;\
-    while (*word == ' ') word++; \
+    while (*word != ' ' && *word) word++;
+    while (*word == ' ') word++;
     while (1) {
 #define do_user_mode(FLAG) do { if (add) user->modes |= FLAG; else user->modes &= ~FLAG; } while (0)
        switch (*mode_change++) {
@@ -2081,6 +2158,14 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
         case 'r': do_chan_mode(MODE_REGONLY); break;
         case 's': do_chan_mode(MODE_SECRET); break;
         case 't': do_chan_mode(MODE_TOPICLIMIT); break;
+       case 'z':
+         if (!(flags & MCP_REGISTERED)) {
+          do_chan_mode(MODE_REGISTERED);
+         } else {
+          mod_chanmode_free(change);
+          return NULL;
+         }
+         break;
 #undef do_chan_mode
         case 'l':
             if (add) {
@@ -2116,7 +2201,7 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
             change->args[ch_arg].mode = MODE_BAN;
             if (!add)
                 change->args[ch_arg].mode |= MODE_REMOVE;
-            change->args[ch_arg++].hostmask = modes[in_arg++];
+            change->args[ch_arg++].u.hostmask = modes[in_arg++];
             break;
         case 'o': case 'v':
         {
@@ -2134,7 +2219,7 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
                 victim = GetUserH(modes[in_arg++]);
             if (!victim)
                 continue;
-            if ((change->args[ch_arg].member = GetUserMode(channel, victim)))
+            if ((change->args[ch_arg].u.member = GetUserMode(channel, victim)))
                 ch_arg++;
             break;
         }
@@ -2224,6 +2309,7 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
+        DO_MODE_CHAR(REGISTERED, 'z');
 #undef DO_MODE_CHAR
         if (change->modes_clear & channel->modes & MODE_KEY)
             mod_chanmode_append(&chbuf, 'k', channel->key);
@@ -2235,13 +2321,13 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
             chbuf.modes[chbuf.modes_used++] = mode = '-';
         switch (change->args[arg].mode & ~MODE_REMOVE) {
         case MODE_BAN:
-            mod_chanmode_append(&chbuf, 'b', change->args[arg].hostmask);
+            mod_chanmode_append(&chbuf, 'b', change->args[arg].u.hostmask);
             break;
         default:
             if (change->args[arg].mode & MODE_CHANOP)
-                mod_chanmode_append(&chbuf, 'o', change->args[arg].member->user->numeric);
+                mod_chanmode_append(&chbuf, 'o', change->args[arg].u.member->user->numeric);
             if (change->args[arg].mode & MODE_VOICE)
-                mod_chanmode_append(&chbuf, 'v', change->args[arg].member->user->numeric);
+                mod_chanmode_append(&chbuf, 'v', change->args[arg].u.member->user->numeric);
             break;
         }
     }
@@ -2262,6 +2348,7 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
+        DO_MODE_CHAR(REGISTERED, 'z');
 #undef DO_MODE_CHAR
         if(change->modes_set & MODE_KEY)
             mod_chanmode_append(&chbuf, 'k', change->new_key);
@@ -2277,13 +2364,13 @@ mod_chanmode_announce(struct userNode *who, struct chanNode *channel, struct mod
             chbuf.modes[chbuf.modes_used++] = mode = '+';
         switch (change->args[arg].mode) {
         case MODE_BAN:
-            mod_chanmode_append(&chbuf, 'b', change->args[arg].hostmask);
+            mod_chanmode_append(&chbuf, 'b', change->args[arg].u.hostmask);
             break;
         default:
             if (change->args[arg].mode & MODE_CHANOP)
-                mod_chanmode_append(&chbuf, 'o', change->args[arg].member->user->numeric);
+                mod_chanmode_append(&chbuf, 'o', change->args[arg].u.member->user->numeric);
             if (change->args[arg].mode & MODE_VOICE)
-                mod_chanmode_append(&chbuf, 'v', change->args[arg].member->user->numeric);
+                mod_chanmode_append(&chbuf, 'v', change->args[arg].u.member->user->numeric);
             break;
         }
     }
@@ -2317,6 +2404,7 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
+        DO_MODE_CHAR(REGISTERED, 'z');
 #undef DO_MODE_CHAR
     }
     if (change->modes_set) {
@@ -2332,6 +2420,7 @@ mod_chanmode_format(struct mod_chanmode *change, char *outbuff)
         DO_MODE_CHAR(REGONLY, 'r');
         DO_MODE_CHAR(NOCOLORS, 'c');
         DO_MODE_CHAR(NOCTCPS, 'C');
+        DO_MODE_CHAR(REGISTERED, 'z');
 #undef DO_MODE_CHAR
         switch (change->modes_set & (MODE_KEY|MODE_LIMIT)) {
         case MODE_KEY|MODE_LIMIT:
@@ -2375,8 +2464,9 @@ clear_chanmode(struct chanNode *channel, const char *modes)
         case 'b': remove |= MODE_BAN; break;
         case 'D': remove |= MODE_DELAYJOINS; break;
         case 'r': remove |= MODE_REGONLY; break;
-        case 'c': remove |= MODE_NOCOLORS;
+        case 'c': remove |= MODE_NOCOLORS; break;
         case 'C': remove |= MODE_NOCTCPS; break;
+        case 'z': remove |= MODE_REGISTERED; break;
         }
     }