summaryrefslogtreecommitdiff
path: root/src/channel_mode.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/channel_mode.c')
-rw-r--r--src/channel_mode.c1797
1 files changed, 1797 insertions, 0 deletions
diff --git a/src/channel_mode.c b/src/channel_mode.c
new file mode 100644
index 0000000..4751ec4
--- /dev/null
+++ b/src/channel_mode.c
@@ -0,0 +1,1797 @@
+/*
+ * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
+ * channel_mode.c: Controls modes on channels.
+ *
+ * Copyright (C) 2005 by the past and present ircd coders, and others.
+ *
+ * This program 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
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * $Id$
+ */
+
+#include "stdinc.h"
+#include "list.h"
+#include "channel.h"
+#include "channel_mode.h"
+#include "client.h"
+#include "hash.h"
+#include "hostmask.h"
+#include "irc_string.h"
+#include "sprintf_irc.h"
+#include "ircd.h"
+#include "numeric.h"
+#include "s_serv.h" /* captab */
+#include "s_user.h"
+#include "send.h"
+#include "whowas.h"
+#include "conf.h" /* ConfigFileEntry, ConfigChannel */
+#include "event.h"
+#include "memory.h"
+#include "balloc.h"
+#include "log.h"
+#include "parse.h"
+
+/* some small utility functions */
+static char *check_string(char *);
+static char *fix_key(char *);
+static char *fix_key_old(char *);
+static void chm_nosuch(struct Client *, struct Client *,
+ struct Channel *, int, int *, char **, int *, int,
+ int, char, void *, const char *);
+static void chm_simple(struct Client *, struct Client *, struct Channel *,
+ int, int *, char **, int *, int, int, char, void *,
+ const char *);
+static void chm_registered(struct Client *, struct Client *, struct Channel *,
+ int, int *, char **, int *, int, int, char, void *,
+ const char *);
+static void chm_limit(struct Client *, struct Client *, struct Channel *,
+ int, int *, char **, int *, int, int, char, void *,
+ const char *);
+static void chm_key(struct Client *, struct Client *, struct Channel *,
+ int, int *, char **, int *, int, int, char, void *,
+ const char *);
+static void chm_op(struct Client *, struct Client *, struct Channel *, int,
+ int *, char **, int *, int, int, char, void *,
+ const char *);
+#ifdef HALFOPS
+static void chm_hop(struct Client *, struct Client *, struct Channel *, int,
+ int *, char **, int *, int, int, char, void *,
+ const char *);
+#endif
+static void chm_voice(struct Client *, struct Client *, struct Channel *,
+ int, int *, char **, int *, int, int, char, void *,
+ const char *);
+static void chm_ban(struct Client *, struct Client *, struct Channel *, int,
+ int *, char **, int *, int, int, char, void *,
+ const char *);
+static void chm_except(struct Client *, struct Client *, struct Channel *,
+ int, int *, char **, int *, int, int, char, void *,
+ const char *);
+static void chm_invex(struct Client *, struct Client *, struct Channel *,
+ int, int *, char **, int *, int, int, char, void *,
+ const char *);
+static void send_cap_mode_changes(struct Client *, struct Client *,
+ struct Channel *, unsigned int, unsigned int);
+static void send_mode_changes(struct Client *, struct Client *,
+ struct Channel *, char *);
+
+/* 10 is a magic number in hybrid 6 NFI where it comes from -db */
+#define BAN_FUDGE 10
+#define NCHCAPS (sizeof(channel_capabs)/sizeof(int))
+#define NCHCAP_COMBOS (1 << NCHCAPS)
+
+static char nuh_mask[MAXPARA][IRCD_BUFSIZE];
+/* some buffers for rebuilding channel/nick lists with ,'s */
+static char modebuf[IRCD_BUFSIZE];
+static char parabuf[MODEBUFLEN];
+static struct ChModeChange mode_changes[IRCD_BUFSIZE];
+static int mode_count;
+static int mode_limit; /* number of modes set other than simple */
+static int simple_modes_mask; /* bit mask of simple modes already set */
+#ifdef HALFOPS
+static int channel_capabs[] = { CAP_EX, CAP_IE, CAP_TS6, CAP_HOPS };
+#else
+static int channel_capabs[] = { CAP_EX, CAP_IE, CAP_TS6 };
+#endif
+static struct ChCapCombo chcap_combos[NCHCAP_COMBOS];
+extern BlockHeap *ban_heap;
+
+
+/* XXX check_string is propably not longer required in add_id and del_id */
+/* check_string()
+ *
+ * inputs - string to check
+ * output - pointer to modified string
+ * side effects - Fixes a string so that the first white space found
+ * becomes an end of string marker (`\0`).
+ * returns the 'fixed' string or "*" if the string
+ * was NULL length or a NULL pointer.
+ */
+static char *
+check_string(char *s)
+{
+ char *str = s;
+ static char star[] = "*";
+
+ if (EmptyString(s))
+ return star;
+
+ for (; *s; ++s)
+ {
+ if (IsSpace(*s))
+ {
+ *s = '\0';
+ break;
+ }
+ }
+
+ return str;
+}
+
+/*
+ * Ban functions to work with mode +b/e/d/I
+ */
+/* add the specified ID to the channel..
+ * -is 8/9/00
+ */
+
+int
+add_id(struct Client *client_p, struct Channel *chptr, char *banid, int type)
+{
+ dlink_list *list = NULL;
+ dlink_node *ban = NULL;
+ size_t len = 0;
+ struct Ban *ban_p = NULL;
+ unsigned int num_mask;
+ char name[NICKLEN + 1];
+ char user[USERLEN + 1];
+ char host[HOSTLEN + 1];
+ struct split_nuh_item nuh;
+
+ /* dont let local clients overflow the b/e/I lists */
+ if (MyClient(client_p))
+ {
+ num_mask = dlink_list_length(&chptr->banlist) +
+ dlink_list_length(&chptr->exceptlist) +
+ dlink_list_length(&chptr->invexlist);
+
+ if (num_mask >= ConfigChannel.max_bans)
+ {
+ sendto_one(client_p, form_str(ERR_BANLISTFULL),
+ me.name, client_p->name, chptr->chname, banid);
+ return 0;
+ }
+
+ collapse(banid);
+ }
+
+ nuh.nuhmask = check_string(banid);
+ nuh.nickptr = name;
+ nuh.userptr = user;
+ nuh.hostptr = host;
+
+ nuh.nicksize = sizeof(name);
+ nuh.usersize = sizeof(user);
+ nuh.hostsize = sizeof(host);
+
+ split_nuh(&nuh);
+
+ /*
+ * Re-assemble a new n!u@h and print it back to banid for sending
+ * the mode to the channel.
+ */
+ len = ircsprintf(banid, "%s!%s@%s", name, user, host);
+
+ switch (type)
+ {
+ case CHFL_BAN:
+ list = &chptr->banlist;
+ clear_ban_cache(chptr);
+ break;
+ case CHFL_EXCEPTION:
+ list = &chptr->exceptlist;
+ clear_ban_cache(chptr);
+ break;
+ case CHFL_INVEX:
+ list = &chptr->invexlist;
+ break;
+ default:
+ assert(0);
+ return 0;
+ }
+
+ DLINK_FOREACH(ban, list->head)
+ {
+ ban_p = ban->data;
+ if (!irccmp(ban_p->name, name) &&
+ !irccmp(ban_p->username, user) &&
+ !irccmp(ban_p->host, host))
+ {
+ return 0;
+ }
+ }
+
+ ban_p = BlockHeapAlloc(ban_heap);
+
+ DupString(ban_p->name, name);
+ DupString(ban_p->username, user);
+ DupString(ban_p->host, host);
+
+ ban_p->when = CurrentTime;
+ ban_p->len = len - 2; /* -2 for @ and ! */
+ ban_p->type = parse_netmask(host, &ban_p->addr, &ban_p->bits);
+
+ if (IsClient(client_p))
+ {
+ ban_p->who = MyMalloc(strlen(client_p->name) +
+ strlen(client_p->username) +
+ strlen(client_p->host) + 3);
+ ircsprintf(ban_p->who, "%s!%s@%s", client_p->name,
+ client_p->username, client_p->host);
+ }
+ else
+ DupString(ban_p->who, client_p->name);
+
+ dlinkAdd(ban_p, &ban_p->node, list);
+
+ return 1;
+}
+
+/*
+ * inputs - pointer to channel
+ * - pointer to ban id
+ * - type of ban, i.e. ban, exception, invex
+ * output - 0 for failure, 1 for success
+ * side effects -
+ */
+static int
+del_id(struct Channel *chptr, char *banid, int type)
+{
+ dlink_list *list;
+ dlink_node *ban;
+ struct Ban *banptr;
+ char name[NICKLEN + 1];
+ char user[USERLEN + 1];
+ char host[HOSTLEN + 1];
+ struct split_nuh_item nuh;
+
+ if (banid == NULL)
+ return 0;
+
+ nuh.nuhmask = check_string(banid);
+ nuh.nickptr = name;
+ nuh.userptr = user;
+ nuh.hostptr = host;
+
+ nuh.nicksize = sizeof(name);
+ nuh.usersize = sizeof(user);
+ nuh.hostsize = sizeof(host);
+
+ split_nuh(&nuh);
+
+ /*
+ * Re-assemble a new n!u@h and print it back to banid for sending
+ * the mode to the channel.
+ */
+ ircsprintf(banid, "%s!%s@%s", name, user, host);
+
+ switch (type)
+ {
+ case CHFL_BAN:
+ list = &chptr->banlist;
+ clear_ban_cache(chptr);
+ break;
+ case CHFL_EXCEPTION:
+ list = &chptr->exceptlist;
+ clear_ban_cache(chptr);
+ break;
+ case CHFL_INVEX:
+ list = &chptr->invexlist;
+ break;
+ default:
+ sendto_realops_flags(UMODE_ALL, L_ALL,
+ "del_id() called with unknown ban type %d!", type);
+ return 0;
+ }
+
+ DLINK_FOREACH(ban, list->head)
+ {
+ banptr = ban->data;
+
+ if (!irccmp(name, banptr->name) &&
+ !irccmp(user, banptr->username) &&
+ !irccmp(host, banptr->host))
+ {
+ remove_ban(banptr, list);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+const struct mode_letter chan_modes[] = {
+ { MODE_INVITEONLY, 'i' },
+ { MODE_MODERATED, 'm' },
+ { MODE_NOPRIVMSGS, 'n' },
+ { MODE_PRIVATE, 'p' },
+ { MODE_REGISTERED, 'r' },
+ { MODE_SECRET, 's' },
+ { MODE_TOPICLIMIT, 't' },
+ { MODE_OPERONLY, 'O' },
+ { MODE_REGONLY, 'R' },
+ { MODE_SSLONLY, 'S' },
+ { 0, '\0' }
+};
+
+/* channel_modes()
+ *
+ * inputs - pointer to channel
+ * - pointer to client
+ * - pointer to mode buf
+ * - pointer to parameter buf
+ * output - NONE
+ * side effects - write the "simple" list of channel modes for channel
+ * chptr onto buffer mbuf with the parameters in pbuf.
+ */
+void
+channel_modes(struct Channel *chptr, struct Client *client_p,
+ char *mbuf, char *pbuf)
+{
+ const struct mode_letter *tab = chan_modes;
+
+ *mbuf++ = '+';
+ *pbuf = '\0';
+
+ for (; tab->mode; ++tab)
+ if (chptr->mode.mode & tab->mode)
+ *mbuf++ = tab->letter;
+
+ if (chptr->mode.limit)
+ {
+ *mbuf++ = 'l';
+
+ if (IsServer(client_p) || HasFlag(client_p, FLAGS_SERVICE) || IsMember(client_p, chptr))
+ pbuf += ircsprintf(pbuf, "%d ", chptr->mode.limit);
+ }
+
+ if (chptr->mode.key[0])
+ {
+ *mbuf++ = 'k';
+
+ if (IsServer(client_p) || HasFlag(client_p, FLAGS_SERVICE) || IsMember(client_p, chptr))
+ ircsprintf(pbuf, "%s ", chptr->mode.key);
+ }
+
+ *mbuf = '\0';
+}
+
+/* fix_key()
+ *
+ * inputs - pointer to key to clean up
+ * output - pointer to cleaned up key
+ * side effects - input string is modified
+ *
+ * stolen from Undernet's ircd -orabidoo
+ */
+static char *
+fix_key(char *arg)
+{
+ unsigned char *s, *t, c;
+
+ for (s = t = (unsigned char *)arg; (c = *s); s++)
+ {
+ c &= 0x7f;
+ if (c != ':' && c > ' ' && c != ',')
+ *t++ = c;
+ }
+
+ *t = '\0';
+ return(arg);
+}
+
+/* fix_key_old()
+ *
+ * inputs - pointer to key to clean up
+ * output - pointer to cleaned up key
+ * side effects - input string is modifed
+ *
+ * Here we attempt to be compatible with older non-hybrid servers.
+ * We can't back down from the ':' issue however. --Rodder
+ */
+static char *
+fix_key_old(char *arg)
+{
+ unsigned char *s, *t, c;
+
+ for (s = t = (unsigned char *)arg; (c = *s); s++)
+ {
+ c &= 0x7f;
+ if ((c != 0x0a) && (c != ':') && (c != 0x0d) && (c != ','))
+ *t++ = c;
+ }
+
+ *t = '\0';
+ return(arg);
+}
+
+/* bitmasks for various error returns that set_channel_mode should only return
+ * once per call -orabidoo
+ */
+
+#define SM_ERR_NOTS 0x00000001 /* No TS on channel */
+#define SM_ERR_NOOPS 0x00000002 /* No chan ops */
+#define SM_ERR_UNKNOWN 0x00000004
+#define SM_ERR_RPL_B 0x00000008
+#define SM_ERR_RPL_E 0x00000010
+#define SM_ERR_NOTONCHANNEL 0x00000020 /* Not on channel */
+#define SM_ERR_RPL_I 0x00000040
+#define SM_ERR_NOTOPER 0x00000080
+#define SM_ERR_ONLYSERVER 0x00000100
+
+/* Now lets do some stuff to keep track of what combinations of
+ * servers exist...
+ * Note that the number of combinations doubles each time you add
+ * something to this list. Each one is only quick if no servers use that
+ * combination, but if the numbers get too high here MODE will get too
+ * slow. I suggest if you get more than 7 here, you consider getting rid
+ * of some and merging or something. If it wasn't for irc+cs we would
+ * probably not even need to bother about most of these, but unfortunately
+ * we do. -A1kmm
+ */
+
+/* void init_chcap_usage_counts(void)
+ *
+ * Inputs - none
+ * Output - none
+ * Side-effects - Initialises the usage counts to zero. Fills in the
+ * chcap_yes and chcap_no combination tables.
+ */
+void
+init_chcap_usage_counts(void)
+{
+ unsigned long m, c, y, n;
+
+ memset(chcap_combos, 0, sizeof(chcap_combos));
+
+ /* For every possible combination */
+ for (m = 0; m < NCHCAP_COMBOS; m++)
+ {
+ /* Check each capab */
+ for (c = y = n = 0; c < NCHCAPS; c++)
+ {
+ if ((m & (1 << c)) == 0)
+ n |= channel_capabs[c];
+ else
+ y |= channel_capabs[c];
+ }
+ chcap_combos[m].cap_yes = y;
+ chcap_combos[m].cap_no = n;
+ }
+}
+
+/* void set_chcap_usage_counts(struct Client *serv_p)
+ * Input: serv_p; The client whose capabs to register.
+ * Output: none
+ * Side-effects: Increments the usage counts for the correct capab
+ * combination.
+ */
+void
+set_chcap_usage_counts(struct Client *serv_p)
+{
+ int n;
+
+ for (n = 0; n < NCHCAP_COMBOS; n++)
+ {
+ if (((serv_p->localClient->caps & chcap_combos[n].cap_yes) ==
+ chcap_combos[n].cap_yes) &&
+ ((serv_p->localClient->caps & chcap_combos[n].cap_no) == 0))
+ {
+ chcap_combos[n].count++;
+ return;
+ }
+ }
+
+ /* This should be impossible -A1kmm. */
+ assert(0);
+}
+
+/* void set_chcap_usage_counts(struct Client *serv_p)
+ *
+ * Inputs - serv_p; The client whose capabs to register.
+ * Output - none
+ * Side-effects - Decrements the usage counts for the correct capab
+ * combination.
+ */
+void
+unset_chcap_usage_counts(struct Client *serv_p)
+{
+ int n;
+
+ for (n = 0; n < NCHCAP_COMBOS; n++)
+ {
+ if ((serv_p->localClient->caps & chcap_combos[n].cap_yes) ==
+ chcap_combos[n].cap_yes &&
+ (serv_p->localClient->caps & chcap_combos[n].cap_no) == 0)
+ {
+ /* Hopefully capabs can't change dynamically or anything... */
+ assert(chcap_combos[n].count > 0);
+ chcap_combos[n].count--;
+ return;
+ }
+ }
+
+ /* This should be impossible -A1kmm. */
+ assert(0);
+}
+
+/* Mode functions handle mode changes for a particular mode... */
+static void
+chm_nosuch(struct Client *client_p, struct Client *source_p,
+ struct Channel *chptr, int parc, int *parn,
+ char **parv, int *errors, int alev, int dir, char c, void *d,
+ const char *chname)
+{
+ if (*errors & SM_ERR_UNKNOWN)
+ return;
+
+ *errors |= SM_ERR_UNKNOWN;
+ sendto_one(source_p, form_str(ERR_UNKNOWNMODE), me.name,
+ source_p->name, c);
+}
+
+static void
+chm_simple(struct Client *client_p, struct Client *source_p, struct Channel *chptr,
+ int parc, int *parn, char **parv, int *errors, int alev, int dir,
+ char c, void *d, const char *chname)
+{
+ long mode_type;
+
+ mode_type = (long)d;
+
+ if ((alev < CHACCESS_HALFOP) ||
+ ((mode_type == MODE_PRIVATE) && (alev < CHACCESS_CHANOP)))
+ {
+ if (!(*errors & SM_ERR_NOOPS))
+ sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
+ ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
+ me.name, source_p->name, chname);
+ *errors |= SM_ERR_NOOPS;
+ return;
+ }
+
+ /* If have already dealt with this simple mode, ignore it */
+ if (simple_modes_mask & mode_type)
+ return;
+
+ simple_modes_mask |= mode_type;
+
+ /* setting + */
+ /* Apparently, (though no one has ever told the hybrid group directly)
+ * admins don't like redundant mode checking. ok. It would have been nice
+ * if you had have told us directly. I've left the original code snippets
+ * in place.
+ *
+ * -Dianora
+ */
+ if ((dir == MODE_ADD)) /* && !(chptr->mode.mode & mode_type)) */
+ {
+ chptr->mode.mode |= mode_type;
+
+ mode_changes[mode_count].letter = c;
+ mode_changes[mode_count].dir = MODE_ADD;
+ mode_changes[mode_count].caps = 0;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].id = NULL;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count++].arg = NULL;
+ }
+ else if ((dir == MODE_DEL)) /* && (chptr->mode.mode & mode_type)) */
+ {
+ /* setting - */
+
+ chptr->mode.mode &= ~mode_type;
+
+ mode_changes[mode_count].letter = c;
+ mode_changes[mode_count].dir = MODE_DEL;
+ mode_changes[mode_count].caps = 0;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count].id = NULL;
+ mode_changes[mode_count++].arg = NULL;
+ }
+}
+
+static void
+chm_registered(struct Client *client_p, struct Client *source_p, struct Channel *chptr,
+ int parc, int *parn, char **parv, int *errors, int alev, int dir,
+ char c, void *d, const char *chname)
+{
+ long mode_type;
+
+ mode_type = (long)d;
+
+
+ if (!IsServer(source_p) && !HasFlag(source_p, FLAGS_SERVICE))
+ {
+ if (!(*errors & SM_ERR_ONLYSERVER))
+ sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
+ ERR_NOTONCHANNEL : ERR_ONLYSERVERSCANCHANGE),
+ me.name, source_p->name, chname);
+ *errors |= SM_ERR_ONLYSERVER;
+ return;
+ }
+
+ /* If have already dealt with this simple mode, ignore it */
+ if (simple_modes_mask & mode_type)
+ return;
+
+ simple_modes_mask |= mode_type;
+
+ /* setting + */
+ /* Apparently, (though no one has ever told the hybrid group directly)
+ * admins don't like redundant mode checking. ok. It would have been nice
+ * if you had have told us directly. I've left the original code snippets
+ * in place.
+ *
+ * -Dianora
+ */
+ if ((dir == MODE_ADD)) /* && !(chptr->mode.mode & mode_type)) */
+ {
+ chptr->mode.mode |= mode_type;
+
+ mode_changes[mode_count].letter = c;
+ mode_changes[mode_count].dir = MODE_ADD;
+ mode_changes[mode_count].caps = 0;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].id = NULL;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count++].arg = NULL;
+ }
+ else if ((dir == MODE_DEL)) /* && (chptr->mode.mode & mode_type)) */
+ {
+ /* setting - */
+
+ chptr->mode.mode &= ~mode_type;
+
+ mode_changes[mode_count].letter = c;
+ mode_changes[mode_count].dir = MODE_DEL;
+ mode_changes[mode_count].caps = 0;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count].id = NULL;
+ mode_changes[mode_count++].arg = NULL;
+ }
+}
+
+static void
+chm_operonly(struct Client *client_p, struct Client *source_p, struct Channel *chptr,
+ int parc, int *parn, char **parv, int *errors, int alev, int dir,
+ char c, void *d, const char *chname)
+{
+ long mode_type;
+
+ mode_type = (long)d;
+
+ if ((alev < CHACCESS_HALFOP) ||
+ ((mode_type == MODE_PRIVATE) && (alev < CHACCESS_CHANOP)))
+ {
+ if (!(*errors & SM_ERR_NOOPS))
+ sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
+ ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
+ me.name, source_p->name, chname);
+ *errors |= SM_ERR_NOOPS;
+ return;
+ }
+ else if (MyClient(source_p) && !HasUMode(source_p, UMODE_OPER))
+ {
+ if (!(*errors & SM_ERR_NOTOPER))
+ {
+ if (alev == CHACCESS_NOTONCHAN)
+ sendto_one(source_p, form_str(ERR_NOTONCHANNEL),
+ me.name, source_p->name, chname);
+ else
+ sendto_one(source_p, form_str(ERR_NOPRIVILEGES),
+ me.name, source_p->name);
+ }
+
+ *errors |= SM_ERR_NOTOPER;
+ return;
+ }
+
+ /* If have already dealt with this simple mode, ignore it */
+ if (simple_modes_mask & mode_type)
+ return;
+
+ simple_modes_mask |= mode_type;
+
+ if ((dir == MODE_ADD)) /* && !(chptr->mode.mode & mode_type)) */
+ {
+ chptr->mode.mode |= mode_type;
+
+ mode_changes[mode_count].letter = c;
+ mode_changes[mode_count].dir = MODE_ADD;
+ mode_changes[mode_count].caps = 0;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].id = NULL;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count++].arg = NULL;
+ }
+ else if ((dir == MODE_DEL)) /* && (chptr->mode.mode & mode_type)) */
+ {
+ /* setting - */
+
+ chptr->mode.mode &= ~mode_type;
+
+ mode_changes[mode_count].letter = c;
+ mode_changes[mode_count].dir = MODE_DEL;
+ mode_changes[mode_count].caps = 0;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count].id = NULL;
+ mode_changes[mode_count++].arg = NULL;
+ }
+}
+
+static void
+chm_ban(struct Client *client_p, struct Client *source_p,
+ struct Channel *chptr, int parc, int *parn,
+ char **parv, int *errors, int alev, int dir, char c, void *d,
+ const char *chname)
+{
+ char *mask = NULL;
+
+ if (dir == MODE_QUERY || parc <= *parn)
+ {
+ dlink_node *ptr = NULL;
+
+ if (*errors & SM_ERR_RPL_B)
+ return;
+
+ *errors |= SM_ERR_RPL_B;
+
+ DLINK_FOREACH(ptr, chptr->banlist.head)
+ {
+ const struct Ban *banptr = ptr->data;
+ sendto_one(client_p, form_str(RPL_BANLIST),
+ me.name, client_p->name, chname,
+ banptr->name, banptr->username, banptr->host,
+ banptr->who, banptr->when);
+ }
+
+ sendto_one(source_p, form_str(RPL_ENDOFBANLIST), me.name,
+ source_p->name, chname);
+ return;
+ }
+
+ if (alev < CHACCESS_HALFOP)
+ {
+ if (!(*errors & SM_ERR_NOOPS))
+ sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
+ ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
+ me.name, source_p->name, chname);
+ *errors |= SM_ERR_NOOPS;
+ return;
+ }
+
+ if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
+ return;
+
+ mask = nuh_mask[*parn];
+ memcpy(mask, parv[*parn], sizeof(nuh_mask[*parn]));
+ ++*parn;
+
+ if (IsServer(client_p))
+ if (strchr(mask, ' '))
+ return;
+
+ switch (dir)
+ {
+ case MODE_ADD:
+ if (!add_id(source_p, chptr, mask, CHFL_BAN))
+ return;
+ break;
+ case MODE_DEL:
+ if (!del_id(chptr, mask, CHFL_BAN))
+ return;
+ break;
+ default:
+ assert(0);
+ }
+
+ mode_changes[mode_count].letter = c;
+ mode_changes[mode_count].dir = dir;
+ mode_changes[mode_count].caps = 0;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count].id = NULL;
+ mode_changes[mode_count++].arg = mask;
+}
+
+static void
+chm_except(struct Client *client_p, struct Client *source_p,
+ struct Channel *chptr, int parc, int *parn,
+ char **parv, int *errors, int alev, int dir, char c, void *d,
+ const char *chname)
+{
+ char *mask = NULL;
+
+ if (alev < CHACCESS_HALFOP)
+ {
+ if (!(*errors & SM_ERR_NOOPS))
+ sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
+ ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
+ me.name, source_p->name, chname);
+ *errors |= SM_ERR_NOOPS;
+ return;
+ }
+
+ if (dir == MODE_QUERY || parc <= *parn)
+ {
+ dlink_node *ptr = NULL;
+
+ if (*errors & SM_ERR_RPL_E)
+ return;
+
+ *errors |= SM_ERR_RPL_E;
+
+ DLINK_FOREACH(ptr, chptr->exceptlist.head)
+ {
+ const struct Ban *banptr = ptr->data;
+ sendto_one(client_p, form_str(RPL_EXCEPTLIST),
+ me.name, client_p->name, chname,
+ banptr->name, banptr->username, banptr->host,
+ banptr->who, banptr->when);
+ }
+
+ sendto_one(source_p, form_str(RPL_ENDOFEXCEPTLIST), me.name,
+ source_p->name, chname);
+ return;
+ }
+
+ if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
+ return;
+
+ mask = nuh_mask[*parn];
+ memcpy(mask, parv[*parn], sizeof(nuh_mask[*parn]));
+ ++*parn;
+
+ if (IsServer(client_p))
+ if (strchr(mask, ' '))
+ return;
+
+ switch (dir)
+ {
+ case MODE_ADD:
+ if (!add_id(source_p, chptr, mask, CHFL_EXCEPTION))
+ return;
+ break;
+ case MODE_DEL:
+ if (!del_id(chptr, mask, CHFL_EXCEPTION))
+ return;
+ break;
+ default:
+ assert(0);
+ }
+
+ mode_changes[mode_count].letter = c;
+ mode_changes[mode_count].dir = dir;
+ mode_changes[mode_count].caps = CAP_EX;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].mems = ONLY_CHANOPS;
+ mode_changes[mode_count].id = NULL;
+ mode_changes[mode_count++].arg = mask;
+}
+
+static void
+chm_invex(struct Client *client_p, struct Client *source_p,
+ struct Channel *chptr, int parc, int *parn,
+ char **parv, int *errors, int alev, int dir, char c, void *d,
+ const char *chname)
+{
+ char *mask = NULL;
+
+ if (alev < CHACCESS_HALFOP)
+ {
+ if (!(*errors & SM_ERR_NOOPS))
+ sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
+ ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
+ me.name, source_p->name, chname);
+ *errors |= SM_ERR_NOOPS;
+ return;
+ }
+
+ if (dir == MODE_QUERY || parc <= *parn)
+ {
+ dlink_node *ptr = NULL;
+
+ if (*errors & SM_ERR_RPL_I)
+ return;
+
+ *errors |= SM_ERR_RPL_I;
+
+ DLINK_FOREACH(ptr, chptr->invexlist.head)
+ {
+ const struct Ban *banptr = ptr->data;
+ sendto_one(client_p, form_str(RPL_INVITELIST), me.name,
+ client_p->name, chname,
+ banptr->name, banptr->username, banptr->host,
+ banptr->who, banptr->when);
+ }
+
+ sendto_one(source_p, form_str(RPL_ENDOFINVITELIST), me.name,
+ source_p->name, chname);
+ return;
+ }
+
+ if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
+ return;
+
+ mask = nuh_mask[*parn];
+ memcpy(mask, parv[*parn], sizeof(nuh_mask[*parn]));
+ ++*parn;
+
+ if (IsServer(client_p))
+ if (strchr(mask, ' '))
+ return;
+
+ switch (dir)
+ {
+ case MODE_ADD:
+ if (!add_id(source_p, chptr, mask, CHFL_INVEX))
+ return;
+ break;
+ case MODE_DEL:
+ if (!del_id(chptr, mask, CHFL_INVEX))
+ return;
+ break;
+ default:
+ assert(0);
+ }
+
+ mode_changes[mode_count].letter = c;
+ mode_changes[mode_count].dir = dir;
+ mode_changes[mode_count].caps = CAP_IE;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].mems = ONLY_CHANOPS;
+ mode_changes[mode_count].id = NULL;
+ mode_changes[mode_count++].arg = mask;
+}
+
+/*
+ * inputs - pointer to channel
+ * output - none
+ * side effects - clear ban cache
+ */
+void
+clear_ban_cache(struct Channel *chptr)
+{
+ dlink_node *ptr = NULL;
+
+ DLINK_FOREACH(ptr, chptr->members.head)
+ {
+ struct Membership *ms = ptr->data;
+
+ if (MyConnect(ms->client_p))
+ ms->flags &= ~(CHFL_BAN_SILENCED|CHFL_BAN_CHECKED);
+ }
+}
+
+void
+clear_ban_cache_client(struct Client *client_p)
+{
+ dlink_node *ptr = NULL;
+
+ DLINK_FOREACH(ptr, client_p->channel.head)
+ {
+ struct Membership *ms = ptr->data;
+ ms->flags &= ~(CHFL_BAN_SILENCED|CHFL_BAN_CHECKED);
+ }
+}
+
+static void
+chm_op(struct Client *client_p, struct Client *source_p,
+ struct Channel *chptr, int parc, int *parn,
+ char **parv, int *errors, int alev, int dir, char c, void *d,
+ const char *chname)
+{
+ char *opnick;
+ struct Client *targ_p;
+ struct Membership *member;
+ int caps = 0;
+
+ if (alev < CHACCESS_CHANOP)
+ {
+ if (!(*errors & SM_ERR_NOOPS))
+ sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
+ ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
+ me.name, source_p->name, chname);
+ *errors |= SM_ERR_NOOPS;
+ return;
+ }
+
+ if ((dir == MODE_QUERY) || (parc <= *parn))
+ return;
+
+ opnick = parv[(*parn)++];
+
+ if ((targ_p = find_chasing(client_p, source_p, opnick, NULL)) == NULL)
+ return;
+ if (!IsClient(targ_p))
+ return;
+
+ if ((member = find_channel_link(targ_p, chptr)) == NULL)
+ {
+ if (!(*errors & SM_ERR_NOTONCHANNEL))
+ sendto_one(source_p, form_str(ERR_USERNOTINCHANNEL),
+ me.name, source_p->name, opnick, chname);
+ *errors |= SM_ERR_NOTONCHANNEL;
+ return;
+ }
+
+ if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
+ return;
+
+ /* no redundant mode changes */
+ if (dir == MODE_ADD && has_member_flags(member, CHFL_CHANOP))
+ return;
+ if (dir == MODE_DEL && !has_member_flags(member, CHFL_CHANOP))
+ {
+#ifdef HALFOPS
+ if (has_member_flags(member, CHFL_HALFOP))
+ {
+ --*parn;
+ chm_hop(client_p, source_p, chptr, parc, parn, parv, errors, alev,
+ dir, c, d, chname);
+ }
+#endif
+ return;
+ }
+
+#ifdef HALFOPS
+ if (dir == MODE_ADD && has_member_flags(member, CHFL_HALFOP))
+ {
+ /* promoting from % to @ is visible only to CAP_HOPS servers */
+ mode_changes[mode_count].letter = 'h';
+ mode_changes[mode_count].dir = MODE_DEL;
+ mode_changes[mode_count].caps = caps = CAP_HOPS;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count].id = NULL;
+ mode_changes[mode_count].arg = targ_p->name;
+ mode_changes[mode_count++].client = targ_p;
+ }
+#endif
+
+ mode_changes[mode_count].letter = 'o';
+ mode_changes[mode_count].dir = dir;
+ mode_changes[mode_count].caps = caps;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count].id = targ_p->id;
+ mode_changes[mode_count].arg = targ_p->name;
+ mode_changes[mode_count++].client = targ_p;
+
+ if (dir == MODE_ADD)
+ {
+ AddMemberFlag(member, CHFL_CHANOP);
+ DelMemberFlag(member, CHFL_DEOPPED | CHFL_HALFOP);
+ }
+ else
+ DelMemberFlag(member, CHFL_CHANOP);
+}
+
+#ifdef HALFOPS
+static void
+chm_hop(struct Client *client_p, struct Client *source_p,
+ struct Channel *chptr, int parc, int *parn,
+ char **parv, int *errors, int alev, int dir, char c, void *d,
+ const char *chname)
+{
+ char *opnick;
+ struct Client *targ_p;
+ struct Membership *member;
+
+ /* *sigh* - dont allow halfops to set +/-h, they could fully control a
+ * channel if there were no ops - it doesnt solve anything.. MODE_PRIVATE
+ * when used with MODE_SECRET is paranoid - cant use +p
+ *
+ * it needs to be optional per channel - but not via +p, that or remove
+ * paranoid.. -- fl_
+ *
+ * +p means paranoid, it is useless for anything else on modern IRC, as
+ * list isn't really usable. If you want to have a private channel these
+ * days, you set it +s. Halfops can no longer remove simple modes when
+ * +p is set (although they can set +p) so it is safe to use this to
+ * control whether they can (de)halfop...
+ */
+ if (alev <
+ ((chptr->mode.mode & MODE_PRIVATE) ? CHACCESS_CHANOP : CHACCESS_HALFOP))
+ {
+ if (!(*errors & SM_ERR_NOOPS))
+ sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
+ ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
+ me.name, source_p->name, chname);
+ *errors |= SM_ERR_NOOPS;
+ return;
+ }
+
+ if ((dir == MODE_QUERY) || (parc <= *parn))
+ return;
+
+ opnick = parv[(*parn)++];
+
+ if ((targ_p = find_chasing(client_p, source_p, opnick, NULL)) == NULL)
+ return;
+ if (!IsClient(targ_p))
+ return;
+
+ if ((member = find_channel_link(targ_p, chptr)) == NULL)
+ {
+ if (!(*errors & SM_ERR_NOTONCHANNEL))
+ sendto_one(source_p, form_str(ERR_USERNOTINCHANNEL),
+ me.name, source_p->name, opnick, chname);
+ *errors |= SM_ERR_NOTONCHANNEL;
+ return;
+ }
+
+ if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
+ return;
+
+ /* no redundant mode changes */
+ if (dir == MODE_ADD && has_member_flags(member, CHFL_HALFOP | CHFL_CHANOP))
+ return;
+ if (dir == MODE_DEL && !has_member_flags(member, CHFL_HALFOP))
+ return;
+
+ mode_changes[mode_count].letter = 'h';
+ mode_changes[mode_count].dir = dir;
+ mode_changes[mode_count].caps = CAP_HOPS;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count].id = targ_p->id;
+ mode_changes[mode_count].arg = targ_p->name;
+ mode_changes[mode_count++].client = targ_p;
+
+ mode_changes[mode_count].letter = 'o';
+ mode_changes[mode_count].dir = dir;
+ mode_changes[mode_count].caps = 0;
+ mode_changes[mode_count].nocaps = CAP_HOPS;
+ mode_changes[mode_count].mems = ONLY_SERVERS;
+ mode_changes[mode_count].id = targ_p->id;
+ mode_changes[mode_count].arg = targ_p->name;
+ mode_changes[mode_count++].client = targ_p;
+
+ if (dir == MODE_ADD)
+ {
+ AddMemberFlag(member, CHFL_HALFOP);
+ DelMemberFlag(member, CHFL_DEOPPED);
+ }
+ else
+ DelMemberFlag(member, CHFL_HALFOP);
+}
+#endif
+
+static void
+chm_voice(struct Client *client_p, struct Client *source_p,
+ struct Channel *chptr, int parc, int *parn,
+ char **parv, int *errors, int alev, int dir, char c, void *d,
+ const char *chname)
+{
+ char *opnick;
+ struct Client *targ_p;
+ struct Membership *member;
+
+ if (alev < CHACCESS_HALFOP)
+ {
+ if (!(*errors & SM_ERR_NOOPS))
+ sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
+ ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
+ me.name, source_p->name, chname);
+ *errors |= SM_ERR_NOOPS;
+ return;
+ }
+
+ if ((dir == MODE_QUERY) || parc <= *parn)
+ return;
+
+ opnick = parv[(*parn)++];
+
+ if ((targ_p = find_chasing(client_p, source_p, opnick, NULL)) == NULL)
+ return;
+ if (!IsClient(targ_p))
+ return;
+
+ if ((member = find_channel_link(targ_p, chptr)) == NULL)
+ {
+ if (!(*errors & SM_ERR_NOTONCHANNEL))
+ sendto_one(source_p, form_str(ERR_USERNOTINCHANNEL),
+ me.name, source_p->name, opnick, chname);
+ *errors |= SM_ERR_NOTONCHANNEL;
+ return;
+ }
+
+ if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
+ return;
+
+ /* no redundant mode changes */
+ if (dir == MODE_ADD && has_member_flags(member, CHFL_VOICE))
+ return;
+ if (dir == MODE_DEL && !has_member_flags(member, CHFL_VOICE))
+ return;
+
+ mode_changes[mode_count].letter = 'v';
+ mode_changes[mode_count].dir = dir;
+ mode_changes[mode_count].caps = 0;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count].id = targ_p->id;
+ mode_changes[mode_count].arg = targ_p->name;
+ mode_changes[mode_count++].client = targ_p;
+
+ if (dir == MODE_ADD)
+ AddMemberFlag(member, CHFL_VOICE);
+ else
+ DelMemberFlag(member, CHFL_VOICE);
+}
+
+static void
+chm_limit(struct Client *client_p, struct Client *source_p,
+ struct Channel *chptr, int parc, int *parn,
+ char **parv, int *errors, int alev, int dir, char c, void *d,
+ const char *chname)
+{
+ int i, limit;
+ char *lstr;
+
+ if (alev < CHACCESS_HALFOP)
+ {
+ if (!(*errors & SM_ERR_NOOPS))
+ sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
+ ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
+ me.name, source_p->name, chname);
+ *errors |= SM_ERR_NOOPS;
+ return;
+ }
+
+ if (dir == MODE_QUERY)
+ return;
+
+ if ((dir == MODE_ADD) && parc > *parn)
+ {
+ lstr = parv[(*parn)++];
+
+ if ((limit = atoi(lstr)) <= 0)
+ return;
+
+ ircsprintf(lstr, "%d", limit);
+
+ /* if somebody sets MODE #channel +ll 1 2, accept latter --fl */
+ for (i = 0; i < mode_count; i++)
+ {
+ if (mode_changes[i].letter == c && mode_changes[i].dir == MODE_ADD)
+ mode_changes[i].letter = 0;
+ }
+
+ mode_changes[mode_count].letter = c;
+ mode_changes[mode_count].dir = MODE_ADD;
+ mode_changes[mode_count].caps = 0;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count].id = NULL;
+ mode_changes[mode_count++].arg = lstr;
+
+ chptr->mode.limit = limit;
+ }
+ else if (dir == MODE_DEL)
+ {
+ if (!chptr->mode.limit)
+ return;
+
+ chptr->mode.limit = 0;
+
+ mode_changes[mode_count].letter = c;
+ mode_changes[mode_count].dir = MODE_DEL;
+ mode_changes[mode_count].caps = 0;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count].id = NULL;
+ mode_changes[mode_count++].arg = NULL;
+ }
+}
+
+static void
+chm_key(struct Client *client_p, struct Client *source_p,
+ struct Channel *chptr, int parc, int *parn,
+ char **parv, int *errors, int alev, int dir, char c, void *d,
+ const char *chname)
+{
+ int i;
+ char *key;
+
+ if (alev < CHACCESS_HALFOP)
+ {
+ if (!(*errors & SM_ERR_NOOPS))
+ sendto_one(source_p, form_str(alev == CHACCESS_NOTONCHAN ?
+ ERR_NOTONCHANNEL : ERR_CHANOPRIVSNEEDED),
+ me.name, source_p->name, chname);
+ *errors |= SM_ERR_NOOPS;
+ return;
+ }
+
+ if (dir == MODE_QUERY)
+ return;
+
+ if ((dir == MODE_ADD) && parc > *parn)
+ {
+ key = parv[(*parn)++];
+
+ if (MyClient(source_p))
+ fix_key(key);
+ else
+ fix_key_old(key);
+
+ if (*key == '\0')
+ return;
+
+ assert(key[0] != ' ');
+ strlcpy(chptr->mode.key, key, sizeof(chptr->mode.key));
+
+ /* if somebody does MODE #channel +kk a b, accept latter --fl */
+ for (i = 0; i < mode_count; i++)
+ {
+ if (mode_changes[i].letter == c && mode_changes[i].dir == MODE_ADD)
+ mode_changes[i].letter = 0;
+ }
+
+ mode_changes[mode_count].letter = c;
+ mode_changes[mode_count].dir = MODE_ADD;
+ mode_changes[mode_count].caps = 0;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count].id = NULL;
+ mode_changes[mode_count++].arg = chptr->mode.key;
+ }
+ else if (dir == MODE_DEL)
+ {
+ if (parc > *parn)
+ (*parn)++;
+
+ if (chptr->mode.key[0] == '\0')
+ return;
+
+ chptr->mode.key[0] = '\0';
+
+ mode_changes[mode_count].letter = c;
+ mode_changes[mode_count].dir = MODE_DEL;
+ mode_changes[mode_count].caps = 0;
+ mode_changes[mode_count].nocaps = 0;
+ mode_changes[mode_count].mems = ALL_MEMBERS;
+ mode_changes[mode_count].id = NULL;
+ mode_changes[mode_count++].arg = "*";
+ }
+}
+
+struct ChannelMode
+{
+ void (*func) (struct Client *client_p, struct Client *source_p,
+ struct Channel *chptr, int parc, int *parn, char **parv,
+ int *errors, int alev, int dir, char c, void *d,
+ const char *chname);
+ void *d;
+};
+
+static struct ChannelMode ModeTable[255] =
+{
+ {chm_nosuch, NULL},
+ {chm_nosuch, NULL}, /* A */
+ {chm_nosuch, NULL}, /* B */
+ {chm_nosuch, NULL}, /* C */
+ {chm_nosuch, NULL}, /* D */
+ {chm_nosuch, NULL}, /* E */
+ {chm_nosuch, NULL}, /* F */
+ {chm_nosuch, NULL}, /* G */
+ {chm_nosuch, NULL}, /* H */
+ {chm_invex, NULL}, /* I */
+ {chm_nosuch, NULL}, /* J */
+ {chm_nosuch, NULL}, /* K */
+ {chm_nosuch, NULL}, /* L */
+ {chm_nosuch, NULL}, /* M */
+ {chm_nosuch, NULL}, /* N */
+ {chm_operonly, (void *) MODE_OPERONLY}, /* O */
+ {chm_nosuch, NULL}, /* P */
+ {chm_nosuch, NULL}, /* Q */
+ {chm_simple, (void *) MODE_REGONLY}, /* R */
+ {chm_simple, (void *) MODE_SSLONLY}, /* S */
+ {chm_nosuch, NULL}, /* T */
+ {chm_nosuch, NULL}, /* U */
+ {chm_nosuch, NULL}, /* V */
+ {chm_nosuch, NULL}, /* W */
+ {chm_nosuch, NULL}, /* X */
+ {chm_nosuch, NULL}, /* Y */
+ {chm_nosuch, NULL}, /* Z */
+ {chm_nosuch, NULL},
+ {chm_nosuch, NULL},
+ {chm_nosuch, NULL},
+ {chm_nosuch, NULL},
+ {chm_nosuch, NULL},
+ {chm_nosuch, NULL},
+ {chm_nosuch, NULL}, /* a */
+ {chm_ban, NULL}, /* b */
+ {chm_nosuch, NULL}, /* c */
+ {chm_nosuch, NULL}, /* d */
+ {chm_except, NULL}, /* e */
+ {chm_nosuch, NULL}, /* f */
+ {chm_nosuch, NULL}, /* g */
+#ifdef HALFOPS
+ {chm_hop, NULL}, /* h */
+#else
+ {chm_nosuch, NULL}, /* h */
+#endif
+ {chm_simple, (void *) MODE_INVITEONLY}, /* i */
+ {chm_nosuch, NULL}, /* j */
+ {chm_key, NULL}, /* k */
+ {chm_limit, NULL}, /* l */
+ {chm_simple, (void *) MODE_MODERATED}, /* m */
+ {chm_simple, (void *) MODE_NOPRIVMSGS}, /* n */
+ {chm_op, NULL}, /* o */
+ {chm_simple, (void *) MODE_PRIVATE}, /* p */
+ {chm_nosuch, NULL}, /* q */
+ {chm_registered, (void *) MODE_REGISTERED}, /* r */
+ {chm_simple, (void *) MODE_SECRET}, /* s */
+ {chm_simple, (void *) MODE_TOPICLIMIT}, /* t */
+ {chm_nosuch, NULL}, /* u */
+ {chm_voice, NULL}, /* v */
+ {chm_nosuch, NULL}, /* w */
+ {chm_nosuch, NULL}, /* x */
+ {chm_nosuch, NULL}, /* y */
+ {chm_nosuch, NULL}, /* z */
+};
+
+/* get_channel_access()
+ *
+ * inputs - pointer to Client struct
+ * - pointer to Membership struct
+ * output - CHACCESS_CHANOP if we should let them have
+ * chanop level access, 0 for peon level access.
+ * side effects - NONE
+ */
+static int
+get_channel_access(struct Client *source_p, struct Membership *member)
+{
+ /* Let hacked servers in for now... */
+ if (!MyClient(source_p))
+ return CHACCESS_CHANOP;
+
+ if (member == NULL)
+ return CHACCESS_NOTONCHAN;
+
+ /* just to be sure.. */
+ assert(source_p == member->client_p);
+
+ if (has_member_flags(member, CHFL_CHANOP))
+ return CHACCESS_CHANOP;
+
+#ifdef HALFOPS
+ if (has_member_flags(member, CHFL_HALFOP))
+ return CHACCESS_HALFOP;
+#endif
+
+ return CHACCESS_PEON;
+}
+
+/* void send_cap_mode_changes(struct Client *client_p,
+ * struct Client *source_p,
+ * struct Channel *chptr, int cap, int nocap)
+ * Input: The client sending(client_p), the source client(source_p),
+ * the channel to send mode changes for(chptr)
+ * Output: None.
+ * Side-effects: Sends the appropriate mode changes to capable servers.
+ *
+ * send_cap_mode_changes() will loop the server list itself, because
+ * at this point in time we have 4 capabs for channels, CAP_IE, CAP_EX,
+ * and a server could support any number of these..
+ * so we make the modebufs per server, tailoring them to each servers
+ * specific demand. Its not very pretty, but its one of the few realistic
+ * ways to handle having this many capabs for channel modes.. --fl_
+ *
+ * Reverted back to my original design, except that we now keep a count
+ * of the number of servers which each combination as an optimisation, so
+ * the capabs combinations which are not needed are not worked out. -A1kmm
+ */
+/* rewritten to ensure parabuf < MODEBUFLEN -db */
+
+static void
+send_cap_mode_changes(struct Client *client_p, struct Client *source_p,
+ struct Channel *chptr, unsigned int cap, unsigned int nocap)
+{
+ int i, mbl, pbl, arglen, nc, mc;
+ int len;
+ const char *arg = NULL;
+ char *parptr;
+ int dir = MODE_QUERY;
+
+ mc = 0;
+ nc = 0;
+ pbl = 0;
+
+ parabuf[0] = '\0';
+ parptr = parabuf;
+
+ if ((cap & CAP_TS6) && source_p->id[0] != '\0')
+ mbl = ircsprintf(modebuf, ":%s TMODE %lu %s ", source_p->id,
+ (unsigned long)chptr->channelts, chptr->chname);
+ else
+ mbl = ircsprintf(modebuf, ":%s MODE %s ", source_p->name,
+ chptr->chname);
+
+ /* loop the list of - modes we have */
+ for (i = 0; i < mode_count; i++)
+ {
+ /* if they dont support the cap we need, or they do support a cap they
+ * cant have, then dont add it to the modebuf.. that way they wont see
+ * the mode
+ */
+ if ((mode_changes[i].letter == 0) ||
+ ((cap & mode_changes[i].caps) != mode_changes[i].caps)
+ || ((nocap & mode_changes[i].nocaps) != mode_changes[i].nocaps))
+ continue;
+
+ arg = "";
+
+ if ((cap & CAP_TS6) && mode_changes[i].id)
+ arg = mode_changes[i].id;
+ if (*arg == '\0')
+ arg = mode_changes[i].arg;
+
+ /* if we're creeping past the buf size, we need to send it and make
+ * another line for the other modes
+ * XXX - this could give away server topology with uids being
+ * different lengths, but not much we can do, except possibly break
+ * them as if they were the longest of the nick or uid at all times,
+ * which even then won't work as we don't always know the uid -A1kmm.
+ */
+ if (arg != NULL)
+ arglen = strlen(arg);
+ else
+ arglen = 0;
+
+ if ((mc == MAXMODEPARAMS) ||
+ ((arglen + mbl + pbl + 2) > IRCD_BUFSIZE) ||
+ (pbl + arglen + BAN_FUDGE) >= MODEBUFLEN)
+ {
+ if (nc != 0)
+ sendto_server(client_p, cap, nocap,
+ "%s %s",
+ modebuf, parabuf);
+ nc = 0;
+ mc = 0;
+
+ if ((cap & CAP_TS6) && source_p->id[0] != '\0')
+ mbl = ircsprintf(modebuf, ":%s MODE %s ", source_p->id,
+ chptr->chname);
+ else
+ mbl = ircsprintf(modebuf, ":%s MODE %s ", source_p->name,
+ chptr->chname);
+
+ pbl = 0;
+ parabuf[0] = '\0';
+ parptr = parabuf;
+ dir = MODE_QUERY;
+ }
+
+ if (dir != mode_changes[i].dir)
+ {
+ modebuf[mbl++] = (mode_changes[i].dir == MODE_ADD) ? '+' : '-';
+ dir = mode_changes[i].dir;
+ }
+
+ modebuf[mbl++] = mode_changes[i].letter;
+ modebuf[mbl] = '\0';
+ nc++;
+
+ if (arg != NULL)
+ {
+ len = ircsprintf(parptr, "%s ", arg);
+ pbl += len;
+ parptr += len;
+ mc++;
+ }
+ }
+
+ if (pbl && parabuf[pbl - 1] == ' ')
+ parabuf[pbl - 1] = 0;
+
+ if (nc != 0)
+ sendto_server(client_p, cap, nocap,
+ "%s %s", modebuf, parabuf);
+}
+
+/* void send_mode_changes(struct Client *client_p,
+ * struct Client *source_p,
+ * struct Channel *chptr)
+ * Input: The client sending(client_p), the source client(source_p),
+ * the channel to send mode changes for(chptr),
+ * mode change globals.
+ * Output: None.
+ * Side-effects: Sends the appropriate mode changes to other clients
+ * and propagates to servers.
+ */
+/* ensure parabuf < MODEBUFLEN -db */
+static void
+send_mode_changes(struct Client *client_p, struct Client *source_p,
+ struct Channel *chptr, char *chname)
+{
+ int i, mbl, pbl, arglen, nc, mc;
+ int len;
+ const char *arg = NULL;
+ char *parptr;
+ int dir = MODE_QUERY;
+
+ /* bail out if we have nothing to do... */
+ if (!mode_count)
+ return;
+
+ if (IsServer(source_p))
+ mbl = ircsprintf(modebuf, ":%s MODE %s ", (IsHidden(source_p) ||
+ ConfigServerHide.hide_servers) ?
+ me.name : source_p->name, chname);
+ else
+ mbl = ircsprintf(modebuf, ":%s!%s@%s MODE %s ", source_p->name,
+ source_p->username, source_p->host, chname);
+
+ mc = 0;
+ nc = 0;
+ pbl = 0;
+
+ parabuf[0] = '\0';
+ parptr = parabuf;
+
+ for (i = 0; i < mode_count; i++)
+ {
+ if (mode_changes[i].letter == 0 ||
+ mode_changes[i].mems == NON_CHANOPS ||
+ mode_changes[i].mems == ONLY_SERVERS)
+ continue;
+
+ arg = mode_changes[i].arg;
+ if (arg != NULL)
+ arglen = strlen(arg);
+ else
+ arglen = 0;
+
+ if ((mc == MAXMODEPARAMS) ||
+ ((arglen + mbl + pbl + 2) > IRCD_BUFSIZE) ||
+ ((arglen + pbl + BAN_FUDGE) >= MODEBUFLEN))
+ {
+ if (mbl && modebuf[mbl - 1] == '-')
+ modebuf[mbl - 1] = '\0';
+
+ if (nc != 0)
+ sendto_channel_local(ALL_MEMBERS, 0, chptr, "%s %s", modebuf, parabuf);
+
+ nc = 0;
+ mc = 0;
+
+ if (IsServer(source_p))
+ mbl = ircsprintf(modebuf, ":%s MODE %s ", me.name, chname);
+ else
+ mbl = ircsprintf(modebuf, ":%s!%s@%s MODE %s ", source_p->name,
+ source_p->username, source_p->host, chname);
+
+ pbl = 0;
+ parabuf[0] = '\0';
+ parptr = parabuf;
+ dir = MODE_QUERY;
+ }
+
+ if (dir != mode_changes[i].dir)
+ {
+ modebuf[mbl++] = (mode_changes[i].dir == MODE_ADD) ? '+' : '-';
+ dir = mode_changes[i].dir;
+ }
+
+ modebuf[mbl++] = mode_changes[i].letter;
+ modebuf[mbl] = '\0';
+ nc++;
+
+ if (arg != NULL)
+ {
+ len = ircsprintf(parptr, "%s ", arg);
+ pbl += len;
+ parptr += len;
+ mc++;
+ }
+ }
+
+ if (pbl && parabuf[pbl - 1] == ' ')
+ parabuf[pbl - 1] = 0;
+
+ if (nc != 0)
+ sendto_channel_local(ALL_MEMBERS, 0, chptr, "%s %s", modebuf, parabuf);
+
+ nc = 0;
+ mc = 0;
+
+ /* Now send to servers... */
+ for (i = 0; i < NCHCAP_COMBOS; i++)
+ if (chcap_combos[i].count != 0)
+ send_cap_mode_changes(client_p, source_p, chptr,
+ chcap_combos[i].cap_yes,
+ chcap_combos[i].cap_no);
+}
+
+/* void set_channel_mode(struct Client *client_p, struct Client *source_p,
+ * struct Channel *chptr, int parc, char **parv,
+ * char *chname)
+ * Input: The client we received this from, the client this originated
+ * from, the channel, the parameter count starting at the modes,
+ * the parameters, the channel name.
+ * Output: None.
+ * Side-effects: Changes the channel membership and modes appropriately,
+ * sends the appropriate MODE messages to the appropriate
+ * clients.
+ */
+void
+set_channel_mode(struct Client *client_p, struct Client *source_p, struct Channel *chptr,
+ struct Membership *member, int parc, char *parv[], char *chname)
+{
+ int dir = MODE_ADD;
+ int parn = 1;
+ int alevel, errors = 0;
+ char *ml = parv[0], c;
+ int table_position;
+
+ mode_count = 0;
+ mode_limit = 0;
+ simple_modes_mask = 0;
+
+ alevel = get_channel_access(source_p, member);
+
+ for (; (c = *ml) != '\0'; ml++)
+ {
+#if 0
+ if(mode_count > 20)
+ break;
+#endif
+ switch (c)
+ {
+ case '+':
+ dir = MODE_ADD;
+ break;
+ case '-':
+ dir = MODE_DEL;
+ break;
+ case '=':
+ dir = MODE_QUERY;
+ break;
+ default:
+ if (c < 'A' || c > 'z')
+ table_position = 0;
+ else
+ table_position = c - 'A' + 1;
+ ModeTable[table_position].func(client_p, source_p, chptr,
+ parc, &parn,
+ parv, &errors, alevel, dir, c,
+ ModeTable[table_position].d,
+ chname);
+ break;
+ }
+ }
+
+ send_mode_changes(client_p, source_p, chptr, chname);
+}