rewrite internal handling of modes.
authorJelmer Vernooij <jelmer@samba.org>
Thu, 31 Jan 2008 17:08:01 +0000 (18:08 +0100)
committerJelmer Vernooij <jelmer@samba.org>
Thu, 31 Jan 2008 17:08:01 +0000 (18:08 +0100)
NEWS
lib/state.c
lib/state.h
testsuite/test-isupport.c

diff --git a/NEWS b/NEWS
index fb0eccb7f35c51c7d66aaec9e6e0e3e6517e9cfb..72a4ee1b076753c2e5c5b46e2199bad47b799047 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -67,6 +67,8 @@ Ctrlproxy 3.0.6 UNRELEASED
        * Actually remove networks from disk when they have been removed in 
          the configuration.
 
+       * Parse removals of banlist entries correctly.
+
 Ctrlproxy 3.0.5 2007-12-07
 
   IMPROVEMENTS
index c9ca73442d2c295258bf818679f22a89f1478bc8..358e513af8e29cc5e730670cba050fc9f3f1aecb 100644 (file)
@@ -158,6 +158,24 @@ static void free_exceptlist(struct channel_state *c)
        c->exceptlist = NULL;
 }
 
+static void free_banlist_entry(struct banlist_entry *be)
+{
+       g_free(be->hostmask);
+       g_free(be->by);
+       g_free(be);
+}
+
+static struct banlist_entry *find_banlist_entry(GList *entries, const char *hostmask)
+{
+       GList *gl;
+       for (gl = entries; gl; gl = gl->next) {
+               struct banlist_entry *be = gl->data;
+               if (!strcmp(be->hostmask, hostmask))
+                       return be;
+       }
+       return NULL;
+}
+
 static void free_banlist(struct channel_state *c)
 {
        GList *g;
@@ -166,10 +184,8 @@ static void free_banlist(struct channel_state *c)
        g = c->banlist;
        while(g) {
                struct banlist_entry *be = g->data;
-               g_free(be->hostmask);
-               g_free(be->by);
                g = g_list_remove(g, be);
-               g_free(be);
+               free_banlist_entry(be);
        }
        c->banlist = NULL;
 }
@@ -790,104 +806,147 @@ gboolean modes_change_mode(irc_modes_t modes, gboolean set, char newmode)
        return TRUE;
 }
 
+static int channel_state_change_mode(struct network_state *s, struct network_nick *by, struct channel_state *c, gboolean set, char mode, char **args)
+{
+       struct network_info *info = &s->info;
+       int args_used = 0;
+
+       if (!is_channel_mode(info, mode)) {
+               network_state_log(LOG_WARNING, s, "Mode '%c' set on channel %s is not in the supported list of channel modes from the server", mode, c->name);
+       }
+
+       if (mode == 'b') { /* Ban */
+               struct banlist_entry *be;
+
+               if (set) {
+                       be = g_new0(struct banlist_entry, 1);
+                       be->time_set = time(NULL);
+                       be->hostmask = g_strdup(args[args_used]);
+                       args_used++;
+                       be->by = g_strdup(by->nick);
+                       c->banlist = g_list_append(c->banlist, be);
+               } else {
+                       be = find_banlist_entry(c->banlist, args[args_used]);
+                       args_used++;
+                       if (be == NULL) {
+                               network_state_log(LOG_WARNING, s, "Unable to remove nonpresent banlist entry '%s'", args[args_used]);
+                               return 1;
+                       }
+                       c->banlist = g_list_remove(c->banlist, be);
+                       free_banlist_entry(be);
+               }
+               return 1;
+       } else if (mode == 'l') { /* Limit */
+               modes_change_mode(c->modes, set, 'l');
+               if (set) {
+                       if (!args[args_used]) {
+                               network_state_log(LOG_WARNING, s, "Mode +l requires argument, but no argument found");
+                               return -1;
+                       }
+                       c->limit = atol(args[args_used]);
+                       args_used++;
+               } else {
+                       c->limit = 0;
+               }
+               return 1;
+       } else if (mode == 'k') {
+               modes_change_mode(c->modes, set, 'k');
+               if (set) {
+                       if (!args[args_used]) {
+                               network_state_log(LOG_WARNING, s, "Mode k requires argument, but no argument found");
+                               return -1;
+                       }
+
+                       g_free(c->key);
+                       c->key = g_strdup(args[args_used]);
+                       args_used++;
+               } else {
+                       g_free(c->key);
+                       c->key = NULL;
+               }
+               return 1;
+       } else if (is_prefix_mode(info, mode)) {
+               struct channel_nick *n = find_channel_nick(c, args[args_used]); args_used++;
+               if (!n) {
+                       network_state_log(LOG_WARNING, s, "Can't set mode %c%c on nick %s on channel %s, because nick does not exist!", set?'+':'-', mode, args[args_used], c->name);
+                       return -1;
+               }
+               if (set) {
+                       if (!modes_set_mode(n->modes, mode)) {
+                               network_state_log(LOG_WARNING, s, "Unable to add mode '%c' to modes %s on nick %s on channel %s", mode, n->modes, args[args_used], c->name);
+                       }
+               } else {
+                       if (!modes_unset_mode(n->modes, mode)) {
+                               network_state_log(LOG_WARNING, s, "Unable to remove mode '%c' from modes %s on nick %s on channel %s", mode, n->modes, args[args_used], c->name);
+                       }
+               }
+               return 1;
+       } else {
+               modes_change_mode(c->modes, set, mode);
+               return 0;
+       }
+}
+
 static void handle_mode(struct network_state *s, const struct line *l)
 {
        /* Format:
         * MODE %|<nick>|<channel> [<mode> [<mode parameters>]] */
-       enum mode_type t = ADD;
+       gboolean t = TRUE;
        int i;
+       int arg = 3;
 
        /* Channel modes */
        if (is_channelname(l->args[1], &s->info)) {
                struct channel_state *c = find_channel(s, l->args[1]);
-               struct channel_nick *n;
-               int arg = 2;
+               struct network_nick *by;
+               int ret;
+               char *by_name;
 
                if (c == NULL) {
                        network_state_log(LOG_WARNING, s, 
                                "Unable to change mode for unknown channel '%s'", l->args[1]);
                        return;
                }
+
+               by_name = line_get_nick(l);
+               by = find_network_nick(s, by_name);
+               g_free(by_name);
                
                for(i = 0; l->args[2][i]; i++) {
                        switch(l->args[2][i]) {
-                               case '+': t = ADD; break;
-                               case '-': t = REMOVE; break;
-                               case 'b': /* Ban */
-                                                 {
-                                                         struct banlist_entry *be = g_new0(struct banlist_entry, 1);
-                                                         be->time_set = time(NULL);
-                                                         be->hostmask = g_strdup(l->args[arg]);
-                                                         be->by = line_get_nick(l);
-                                                         c->banlist = g_list_append(c->banlist, be);
-                                                 }
-                                                                                               
-                                                 arg++;
-                                                 break;
-                               case 'l':
-                                       c->modes[(unsigned char)'l'] = t;
-                                   if (t) {
-                                               if (!l->args[++arg]) {
-                                                       network_state_log(LOG_WARNING, s, "Mode +l requires argument, but no argument found");
-                                                       break;
-                                               }
-                                               c->limit = atol(l->args[arg]);
-                                       } else {
-                                               c->limit = 0;
-                                       }
-                                       break;
-                               case 'k':
-                                       c->modes[(unsigned char)'k'] = t;
-                                       if (t) {
-                                               if (!l->args[++arg]) {
-                                                       network_state_log(LOG_WARNING, s, "Mode k requires argument, but no argument found");
-                                                       break;
-                                               }
-
-                                               g_free(c->key);
-                                               c->key = g_strdup(l->args[arg]);
-                                       } else {
-                                               g_free(c->key);
-                                               c->key = NULL;
-                                       }
-
-                                       break;
+                               case '+': t = TRUE; break;
+                               case '-': t = FALSE; break;
                                default:
-                                         if (is_channel_mode(&s->info, l->args[2][i])) {
-                                                 c->modes[(unsigned char)l->args[2][i]] = t;
-                                         } else if (is_user_mode(&s->info, l->args[2][i])) {
-                                                       n = find_channel_nick(c, l->args[++arg]);
-                                                       if (!n) {
-                                                               network_state_log(LOG_WARNING, s, "Can't set mode %c%c on nick %s on channel %s, because nick does not exist!", t == ADD?'+':'-', l->args[2][i], l->args[arg], l->args[1]);
-                                                               break;
-                                                       }
-                                                       if (t == ADD) {
-                                                               if (!modes_set_mode(n->modes, l->args[2][i])) {
-                                                                       network_state_log(LOG_WARNING, s, "Unable to add mode '%c' to modes %s on nick %s on channel %s", l->args[2][i], n->modes, l->args[arg], l->args[1]);
-                                                               }
-                                                       } else {
-                                                               if (!modes_unset_mode(n->modes, l->args[2][i])) {
-                                                                       network_state_log(LOG_WARNING, s, "Unable to remove mode '%c' from modes %s on nick %s on channel %s", l->args[2][i], n->modes, l->args[arg], l->args[1]);
-                                                               }
-                                                       }
-                                         }
+                                         ret = channel_state_change_mode(s, by, c, t, 
+                                                                                                         l->args[2][i],
+                                                                                                         l->args+arg*sizeof(char *));
+                                         if (ret == -1)
+                                                 return;
+                                         arg += ret;
                                          break;
                        }
                }
+
                /* User modes */
        } else {
                struct network_nick *nn = find_add_network_nick(s, l->args[1]);
 
                for(i = 0; l->args[2][i]; i++) {
                        switch(l->args[2][i]) {
-                               case '+': t = ADD;break;
-                               case '-': t = REMOVE; break;
+                               case '+': t = TRUE;break;
+                               case '-': t = FALSE; break;
                                default:
                                          modes_change_mode(nn->modes, t, l->args[2][i]);
                                          break;
                        }
                }
        }
+
+       if (l->args[arg] != NULL) {
+               network_state_log(LOG_WARNING, s, 
+                                                 "mode %s argument not consumed: %s", l->args[2], 
+                                                 l->args[arg]);
+       }
 }
 
 static void handle_001(struct network_state *s, const struct line *l)
@@ -1169,4 +1228,8 @@ char *mode2string(irc_modes_t modes)
        }
 }
 
+gboolean is_prefix_mode(const struct network_info *info, char mode)
+{
+       return get_prefix_by_mode(mode, info) != ' ';
+}
 
index 7344ad3d0102350bf0af758bce5e1a72d05c7e48..e67039b458fc986921a33f1880a7f01ead632af5 100644 (file)
@@ -140,5 +140,6 @@ G_MODULE_EXPORT gboolean is_channel_mode(struct network_info *info, char mode);
 G_MODULE_EXPORT gboolean is_user_mode(struct network_info *info, char mode);
 G_MODULE_EXPORT char get_mode_by_prefix(char prefix, const struct network_info *n);
 G_MODULE_EXPORT char get_prefix_by_mode(char mode, const struct network_info *n);
+G_MODULE_EXPORT gboolean is_prefix_mode(const struct network_info *info, char mode);
 
 #endif /* __CTRLPROXY_STATE_H__ */
index bcd56a518c26092ed7ce5b92e8c315bd38dbc442..f7c6f15ab072388a0f54fb7178e97f5896ccebbb 100644 (file)
@@ -62,6 +62,15 @@ START_TEST(isupport_prefixbymode)
        fail_if (get_prefix_by_mode('x', &ni) != ' ');
 END_TEST
 
+START_TEST(isupport_is_prefix_mode)
+       struct network_info ni = {
+               .prefix = "(ov)@+"
+       };
+       fail_unless (is_prefix_mode(&ni, 'o'));
+       fail_unless (is_prefix_mode(&ni, 'v'));
+       fail_if (is_prefix_mode(&ni, ' '));
+END_TEST
+
 START_TEST(isupport_modebyprefix)
        struct network_info ni = {
                .prefix = "(ov)@+"
@@ -115,5 +124,6 @@ Suite *isupport_suite(void)
        tcase_add_test(tc_core, isupport_info_parse_casemapping);
        tcase_add_test(tc_core, isupport_info_parse_name);
        tcase_add_test(tc_core, isupport_mode_is_channelmode);
+       tcase_add_test(tc_core, isupport_is_prefix_mode);
        return s;
 }