Fix various mode-related bugs (including SF#2964782 and SF#2966959).
[srvx.git] / src / proto-p10.c
index 48b65274fa8f1bc7a350616fb921318e3f8494db..3d291a5bbd50594ab728e9eb9e89c9ff2fc367ab 100644 (file)
@@ -291,9 +291,10 @@ static unsigned int num_notice_funcs;
 static struct dict *unbursted_channels;
 static const char *his_servername;
 static const char *his_servercomment;
+static struct channelList dead_channels;
 
 /* These correspond to 1 << X:      012345678901234567 */
-const char irc_user_mode_chars[] = "o iw dkgn        I";
+const char irc_user_mode_chars[] = "o iw dkgn    x   I";
 
 static struct userNode *AddUser(struct server* uplink, const char *nick, const char *ident, const char *hostname, const char *modes, const char *numeric, const char *userinfo, unsigned long timestamp, const char *realip);
 
@@ -1358,6 +1359,7 @@ static CMD_FUNC(cmd_mode)
             log_module(MAIN_LOG, LOG_ERROR, "Unable to find user %s whose mode is changing.", argv[1]);
             return 0;
         }
+        argv[2] = unsplit_string(argv + 2, argc - 2, NULL);
         mod_usermode(un, argv[2]);
         return 1;
     }
@@ -1832,6 +1834,7 @@ init_parse(void)
     memset(notice_funcs, 0, sizeof(privmsg_func_t)*num_notice_funcs);
 
     userList_init(&dead_users);
+    channelList_init(&dead_channels);
     reg_del_channel_func(remove_unbursted_channel);
     reg_exit_func(parse_cleanup);
 }
@@ -1868,6 +1871,9 @@ parse_line(char *line, int recursive)
         for (i=0; i<dead_users.used; i++)
             free_user(dead_users.list[i]);
         dead_users.used = 0;
+        for (i=0; i<dead_channels.used; i++)
+            UnlockChannel(dead_channels.list[i]);
+        dead_channels.used = 0;
     }
     return res;
 }
@@ -2316,6 +2322,7 @@ void mod_usermode(struct userNode *user, const char *mode_change) {
                 char mask[MAXLEN];
                 char *host, *ident;
                 unsigned int ii;
+
                 for (ii=0; (*word != ' ') && (*word != '\0'); )
                     mask[ii++] = *word++;
                 mask[ii] = 0;
@@ -2329,6 +2336,7 @@ void mod_usermode(struct userNode *user, const char *mode_change) {
                     ident = NULL;
                     host = mask;
                 }
+                user->modes |= FLAGS_HIDDEN_HOST;
                 assign_fakehost(user, host, ident, 0, 0);
             }
             break;
@@ -2425,6 +2433,8 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
             }
             break;
         case 'U':
+            if (flags & MCP_NO_APASS)
+                goto error;
             if (add)
             {
                 if ((in_arg >= argc)
@@ -2441,6 +2451,8 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
             }
             break;
         case 'A':
+            if (flags & MCP_NO_APASS)
+                goto error;
             if (add) {
                 if ((in_arg >= argc)
                     || keyncpy(change->new_apass, modes[in_arg++], sizeof(change->new_apass)))
@@ -2527,6 +2539,11 @@ mod_chanmode_parse(struct chanNode *channel, char **modes, unsigned int argc, un
         change->modes_set &= ~(MODE_SECRET);
         change->modes_clear |= MODE_SECRET;
     }
+    if (change->modes_clear & MODE_REGISTERED) {
+        /* Horribly cheat by using the lock/unlock semantics. */
+        LockChannel(channel);
+        channelList_append(&dead_channels, channel);
+    }
     return change;
   error:
     mod_chanmode_free(change);
@@ -2878,8 +2895,8 @@ reg_notice_func(struct userNode *user, privmsg_func_t handler)
     if (numeric >= num_notice_funcs) {
         int newnum = numeric + 8, ii;
         notice_funcs = realloc(notice_funcs, newnum*sizeof(privmsg_func_t));
-        for (ii = num_privmsg_funcs; ii < newnum; ++ii)
-            privmsg_funcs[ii] = NULL;
+        for (ii = num_notice_funcs; ii < newnum; ++ii)
+            notice_funcs[ii] = NULL;
         num_notice_funcs = newnum;
     }
     if (notice_funcs[numeric])