mirror of
https://github.com/anope/anope.git
synced 2026-06-28 12:56:39 +02:00
BUILD : 1.7.21 (1426) BUGS : 876 NOTES : We now support CIDR in channel ban/invite/except lists. Introduces new CIDR capable generic lists system.
git-svn-id: svn://svn.anope.org/anope/trunk@1426 31f1291d-b8d6-0310-a050-a5561fc1590b git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@1141 5417fbe8-f217-4b02-8779-1006273d7864
This commit is contained in:
parent
482a8aa766
commit
790dc8a4bf
@@ -3,6 +3,7 @@ Anope Version S V N
|
||||
01/24 A Added a check for hosts too long in BotServs bots. [ #00]
|
||||
02/12 A Added SUSPENDED to NS LIST keywords. [#869]
|
||||
02/20 A Added NS SASET LANGUAGE. [#872]
|
||||
09/03 A Added CIDR support in channel bans/excepts. [#876]
|
||||
01/15 F BOT_NOT_ASSIGNED language string error. [#828]
|
||||
01/15 F listchans now shows if a channel is suspended. [#825]
|
||||
01/15 F listnicks now shows if a nickname is suspended. [#825]
|
||||
|
||||
@@ -141,6 +141,19 @@ E void do_mass_mode(char *modes);
|
||||
E void chan_set_correct_modes(User * user, Channel * c, int give_modes);
|
||||
E void restore_unsynced_topics(void);
|
||||
|
||||
E Entry *entry_create(char *mask);
|
||||
E Entry *entry_add(EList *list, char *mask);
|
||||
E void entry_delete(EList *list, Entry *e);
|
||||
E EList *list_create();
|
||||
E int entry_match(Entry *e, char *nick, char *user, char *host, uint32 ip);
|
||||
E int entry_match_mask(Entry *e, char *mask, uint32 ip);
|
||||
E Entry *elist_match(EList *list, char *nick, char *user, char *host, uint32 ip);
|
||||
E Entry *elist_match_mask(EList *list, char *mask, uint32 ip);
|
||||
E Entry *elist_match_user(EList *list, User *u);
|
||||
E Entry *elist_find_mask(EList *list, char *mask);
|
||||
E long get_memuse(EList *list);
|
||||
|
||||
|
||||
#define whosends(ci) ((!(ci) || !((ci)->botflags & BS_SYMBIOSIS) || !(ci)->bi || !(ci)->c || (ci)->c->usercount < BSMinUsers) ? s_ChanServ : (ci)->bi->nick)
|
||||
|
||||
/**** chanserv.c ****/
|
||||
@@ -862,6 +875,14 @@ E void ntoa(struct in_addr addr, char *ipaddr, int len);
|
||||
E char **buildStringList(char *src, int *number);
|
||||
E void binary_to_hex(unsigned char *bin, char *hex, int length);
|
||||
|
||||
E uint32 cidr_to_netmask(uint16 cidr);
|
||||
E uint16 netmask_to_cidr(uint32 mask);
|
||||
|
||||
E int str_is_wildcard(const char *str);
|
||||
E int str_is_pure_wildcard(const char *str);
|
||||
|
||||
E uint32 str_is_ip(char *str);
|
||||
E int str_is_cidr(char *str, uint32 * ip, uint32 * mask, char **host);
|
||||
|
||||
|
||||
/**** modules.c ****/
|
||||
|
||||
+32
-7
@@ -217,6 +217,8 @@ extern int shutdown(int, int);
|
||||
typedef struct server_ Server;
|
||||
typedef struct user_ User;
|
||||
typedef struct channel_ Channel;
|
||||
typedef struct c_elist EList;
|
||||
typedef struct c_elist_entry Entry;
|
||||
typedef struct ModuleData_ ModuleData; /* ModuleData struct */
|
||||
typedef struct memo_ Memo;
|
||||
typedef struct nickrequest_ NickRequest;
|
||||
@@ -356,12 +358,15 @@ struct ircdvars_ {
|
||||
int p10; /* ircd is P10 */
|
||||
char *nickchars; /* character set */
|
||||
int sync; /* reports sync state */
|
||||
int cidrchanbei; /* channel bans/excepts/invites support CIDR (syntax: +b *!*@192.168.0.0/15)
|
||||
* 0 for no support, 1 for strict cidr support, anything else
|
||||
* for ircd specific support (nefarious only cares about first /mask) */
|
||||
};
|
||||
|
||||
struct ircdcapab_ {
|
||||
uint32 noquit;
|
||||
uint32 tsmode;
|
||||
uint32 unconnect;
|
||||
uint32 unconnect;
|
||||
uint32 nickip;
|
||||
uint32 nsjoin;
|
||||
uint32 zip;
|
||||
@@ -962,6 +967,16 @@ struct userdata_ {
|
||||
int16 times;
|
||||
};
|
||||
|
||||
/* Channelban type flags */
|
||||
#define ENTRYTYPE_NONE 0x00000000
|
||||
#define ENTRYTYPE_CIDR4 0x00000001
|
||||
#define ENTRYTYPE_NICK_WILD 0x00000004
|
||||
#define ENTRYTYPE_NICK 0x00000008
|
||||
#define ENTRYTYPE_USER_WILD 0x00000010
|
||||
#define ENTRYTYPE_USER 0x00000020
|
||||
#define ENTRYTYPE_HOST_WILD 0x00000040
|
||||
#define ENTRYTYPE_HOST 0x00000080
|
||||
|
||||
struct channel_ {
|
||||
Channel *next, *prev;
|
||||
char name[CHANMAX];
|
||||
@@ -975,12 +990,9 @@ struct channel_ {
|
||||
char *key; /* NULL if none */
|
||||
char *redirect; /* +L; NULL if none */
|
||||
char *flood; /* +f; NULL if none */
|
||||
int32 bancount, bansize;
|
||||
char **bans;
|
||||
int32 exceptcount, exceptsize;
|
||||
char **excepts;
|
||||
int32 invitecount, invitesize;
|
||||
char **invite;
|
||||
EList *bans;
|
||||
EList *excepts;
|
||||
EList *invites;
|
||||
struct c_userlist {
|
||||
struct c_userlist *next, *prev;
|
||||
User *user;
|
||||
@@ -998,6 +1010,19 @@ struct channel_ {
|
||||
int16 topic_sync; /* Is the topic in sync? */
|
||||
};
|
||||
|
||||
struct c_elist {
|
||||
Entry *entries;
|
||||
int32 count;
|
||||
};
|
||||
|
||||
struct c_elist_entry {
|
||||
Entry *next, *prev;
|
||||
uint32 type;
|
||||
uint32 cidr_ip; /* IP mask for CIDR matching */
|
||||
uint32 cidr_mask; /* Netmask for CIDR matching */
|
||||
char *nick, *user, *host, *mask;
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/* Constants for news types. */
|
||||
|
||||
+21
-38
@@ -130,13 +130,13 @@ void sqline(char *mask, char *reason)
|
||||
*/
|
||||
void common_unban(ChannelInfo * ci, char *nick)
|
||||
{
|
||||
int count, i;
|
||||
char *av[4], **bans;
|
||||
int ac;
|
||||
char buf[BUFSIZE];
|
||||
User *u;
|
||||
char *av[4];
|
||||
char *host = NULL;
|
||||
int matchfound = 0;
|
||||
char buf[BUFSIZE];
|
||||
int ac;
|
||||
uint32 ip = 0;
|
||||
User *u;
|
||||
Entry *ban, *next;
|
||||
|
||||
if (!ci || !ci->c || !nick) {
|
||||
return;
|
||||
@@ -146,6 +146,9 @@ void common_unban(ChannelInfo * ci, char *nick)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ci->c->bans || (ci->c->bans->count == 0))
|
||||
return;
|
||||
|
||||
if (u->hostip == NULL) {
|
||||
host = host_resolve(u->host);
|
||||
/* we store the just resolved hostname so we don't
|
||||
@@ -156,6 +159,9 @@ void common_unban(ChannelInfo * ci, char *nick)
|
||||
} else {
|
||||
host = sstrdup(u->hostip);
|
||||
}
|
||||
/* Convert the host to an IP.. */
|
||||
if (host)
|
||||
ip = str_is_ip(host);
|
||||
|
||||
if (ircd->svsmode_unban) {
|
||||
anope_cmd_unban(ci->name, nick);
|
||||
@@ -171,44 +177,21 @@ void common_unban(ChannelInfo * ci, char *nick)
|
||||
av[1] = sstrdup("-b");
|
||||
ac = 3;
|
||||
}
|
||||
count = ci->c->bancount;
|
||||
bans = scalloc(sizeof(char *) * count, 1);
|
||||
memcpy(bans, ci->c->bans, sizeof(char *) * count);
|
||||
for (i = 0; i < count; i++) {
|
||||
if (match_usermask(bans[i], u)) {
|
||||
anope_cmd_mode(whosends(ci), ci->name, "-b %s", bans[i]);
|
||||
|
||||
for (ban = ci->c->bans->entries; ban; ban = next) {
|
||||
next = ban->next;
|
||||
if (entry_match(ban, u->nick, u->username, u->host, ip) ||
|
||||
entry_match(ban, u->nick, u->username, u->vhost, ip)) {
|
||||
anope_cmd_mode(whosends(ci), ci->name, "-b %s", ban->mask);
|
||||
if (ircdcap->tsmode)
|
||||
av[3] = bans[i];
|
||||
av[3] = ban->mask;
|
||||
else
|
||||
av[2] = bans[i];
|
||||
av[2] = ban->mask;
|
||||
|
||||
do_cmode(whosends(ci), ac, av);
|
||||
matchfound++;
|
||||
}
|
||||
if (host) {
|
||||
/* prevent multiple unbans if the first one was successful in
|
||||
locating the ban for us. This is due to match_userip() checks
|
||||
the vhost again, and thus can return false positive results
|
||||
for the function. but won't prevent thus from clearing out
|
||||
the bans against an IP address since the first would fail and
|
||||
the second would match - TSL
|
||||
*/
|
||||
if (!matchfound) {
|
||||
if (match_userip(bans[i], u, host)) {
|
||||
anope_cmd_mode(whosends(ci), ci->name, "-b %s",
|
||||
bans[i]);
|
||||
if (ircdcap->tsmode)
|
||||
av[3] = bans[i];
|
||||
else
|
||||
av[2] = bans[i];
|
||||
|
||||
do_cmode(whosends(ci), ac, av);
|
||||
}
|
||||
}
|
||||
}
|
||||
matchfound = 0;
|
||||
}
|
||||
free(bans);
|
||||
|
||||
if (ircdcap->tsmode)
|
||||
free(av[2]);
|
||||
else
|
||||
|
||||
+9
-24
@@ -757,25 +757,17 @@ static UserData *get_user_data(Channel * c, User * u)
|
||||
|
||||
void bot_join(ChannelInfo * ci)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!ci || !ci->c || !ci->bi)
|
||||
return;
|
||||
|
||||
if (BSSmartJoin) {
|
||||
/* We check for bans */
|
||||
int count = ci->c->bancount;
|
||||
if (count) {
|
||||
char botmask[BUFSIZE];
|
||||
if (ci->c->bans && ci->c->bans->count) {
|
||||
char buf[BUFSIZE];
|
||||
char **bans = scalloc(sizeof(char *) * count, 1);
|
||||
char *av[4];
|
||||
Entry *ban, *next;
|
||||
int ac;
|
||||
|
||||
memcpy(bans, ci->c->bans, sizeof(char *) * count);
|
||||
snprintf(botmask, sizeof(botmask), "%s!%s@%s", ci->bi->nick,
|
||||
ci->bi->user, ci->bi->host);
|
||||
|
||||
if (ircdcap->tsmode) {
|
||||
snprintf(buf, BUFSIZE - 1, "%ld", (long int) time(NULL));
|
||||
av[0] = ci->c->name;
|
||||
@@ -788,21 +780,16 @@ void bot_join(ChannelInfo * ci)
|
||||
ac = 3;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (match_wild_nocase(ci->c->bans[i], botmask)) {
|
||||
anope_cmd_mode(ci->bi->nick, ci->name, "-b %s",
|
||||
bans[i]);
|
||||
for (ban = ci->c->bans->entries; ban; ban = next) {
|
||||
next = ban->next;
|
||||
if (entry_match(ban, ci->bi->nick, ci->bi->user, ci->bi->host, 0)) {
|
||||
anope_cmd_mode(whosends(ci), ci->name, "-b %s", ban->mask);
|
||||
if (ircdcap->tsmode)
|
||||
av[3] = sstrdup(bans[i]);
|
||||
av[3] = ban->mask;
|
||||
else
|
||||
av[2] = sstrdup(bans[i]);
|
||||
av[2] = ban->mask;
|
||||
|
||||
do_cmode(ci->bi->nick, ac, av);
|
||||
|
||||
if (ircdcap->tsmode)
|
||||
free(av[3]);
|
||||
else
|
||||
free(av[2]);
|
||||
do_cmode(whosends(ci), ac, av);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -810,8 +797,6 @@ void bot_join(ChannelInfo * ci)
|
||||
free(av[2]);
|
||||
else
|
||||
free(av[1]);
|
||||
|
||||
free(bans);
|
||||
}
|
||||
|
||||
/* Should we be invited? */
|
||||
|
||||
+486
-112
@@ -427,7 +427,7 @@ void get_channel_stats(long *nrec, long *memuse)
|
||||
Channel *chan;
|
||||
struct c_userlist *cu;
|
||||
BanData *bd;
|
||||
int i, j;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 1024; i++) {
|
||||
for (chan = chanlist[i]; chan; chan = chan->next) {
|
||||
@@ -445,24 +445,12 @@ void get_channel_stats(long *nrec, long *memuse)
|
||||
if (chan->redirect)
|
||||
mem += strlen(chan->redirect) + 1;
|
||||
}
|
||||
mem += sizeof(char *) * chan->bansize;
|
||||
for (j = 0; j < chan->bancount; j++) {
|
||||
if (chan->bans[j])
|
||||
mem += strlen(chan->bans[j]) + 1;
|
||||
}
|
||||
mem += get_memuse(chan->bans);
|
||||
if (ircd->except) {
|
||||
mem += sizeof(char *) * chan->exceptsize;
|
||||
for (j = 0; j < chan->exceptcount; j++) {
|
||||
if (chan->excepts[j])
|
||||
mem += strlen(chan->excepts[j]) + 1;
|
||||
}
|
||||
mem += get_memuse(chan->excepts);
|
||||
}
|
||||
if (ircd->invitemode) {
|
||||
mem += sizeof(char *) * chan->invitesize;
|
||||
for (j = 0; j < chan->invitecount; j++) {
|
||||
if (chan->invite[j])
|
||||
mem += strlen(chan->invite[j]) + 1;
|
||||
}
|
||||
mem += get_memuse(chan->invites);
|
||||
}
|
||||
for (cu = chan->users; cu; cu = cu->next) {
|
||||
mem += sizeof(*cu);
|
||||
@@ -1270,6 +1258,7 @@ void do_topic(const char *source, int ac, char **av)
|
||||
|
||||
void add_ban(Channel * chan, char *mask)
|
||||
{
|
||||
Entry *ban;
|
||||
/* check for NULL values otherwise we will segfault */
|
||||
if (!chan || !mask) {
|
||||
if (debug) {
|
||||
@@ -1278,25 +1267,28 @@ void add_ban(Channel * chan, char *mask)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if the list already exists, if not create it.
|
||||
* Create a new ban and add it to the list.. ~ Viper */
|
||||
if (!chan->bans)
|
||||
chan->bans = list_create();
|
||||
ban = entry_add(chan->bans, mask);
|
||||
|
||||
if (!ban)
|
||||
fatal("Creating new ban entry failed");
|
||||
|
||||
/* Check whether it matches a botserv bot after adding internally
|
||||
* and parsing it through cidr support. ~ Viper */
|
||||
if (s_BotServ && BSSmartJoin && chan->ci && chan->ci->bi
|
||||
&& chan->usercount >= BSMinUsers) {
|
||||
char botmask[BUFSIZE];
|
||||
BotInfo *bi = chan->ci->bi;
|
||||
|
||||
snprintf(botmask, sizeof(botmask), "%s!%s@%s", bi->nick, bi->user,
|
||||
bi->host);
|
||||
if (match_wild_nocase(mask, botmask)) {
|
||||
if (entry_match(ban, bi->nick, bi->user, bi->host, 0)) {
|
||||
anope_cmd_mode(bi->nick, chan->name, "-b %s", mask);
|
||||
entry_delete(chan->bans, ban);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (chan->bancount >= chan->bansize) {
|
||||
chan->bansize += 8;
|
||||
chan->bans = srealloc(chan->bans, sizeof(char *) * chan->bansize);
|
||||
}
|
||||
chan->bans[chan->bancount++] = sstrdup(mask);
|
||||
|
||||
if (debug)
|
||||
alog("debug: Added ban %s to channel %s", mask, chan->name);
|
||||
}
|
||||
@@ -1305,18 +1297,22 @@ void add_ban(Channel * chan, char *mask)
|
||||
|
||||
void add_exception(Channel * chan, char *mask)
|
||||
{
|
||||
Entry *exception;
|
||||
|
||||
if (!chan || !mask) {
|
||||
if (debug)
|
||||
alog("debug: add_exception called with NULL values");
|
||||
return;
|
||||
}
|
||||
|
||||
if (chan->exceptcount >= chan->exceptsize) {
|
||||
chan->exceptsize += 8;
|
||||
chan->excepts =
|
||||
srealloc(chan->excepts, sizeof(char *) * chan->exceptsize);
|
||||
}
|
||||
chan->excepts[chan->exceptcount++] = sstrdup(mask);
|
||||
/* Check if the list already exists, if not create it.
|
||||
* Create a new exception and add it to the list.. ~ Viper */
|
||||
if (!chan->excepts)
|
||||
chan->excepts = list_create();
|
||||
exception = entry_add(chan->excepts, mask);
|
||||
|
||||
if (!exception)
|
||||
fatal("Creating new exception entry failed");
|
||||
|
||||
if (debug)
|
||||
alog("debug: Added except %s to channel %s", mask, chan->name);
|
||||
@@ -1326,18 +1322,22 @@ void add_exception(Channel * chan, char *mask)
|
||||
|
||||
void add_invite(Channel * chan, char *mask)
|
||||
{
|
||||
Entry *invite;
|
||||
|
||||
if (!chan || !mask) {
|
||||
if (debug)
|
||||
alog("debug: add_invite called with NULL values");
|
||||
return;
|
||||
}
|
||||
|
||||
if (chan->invitecount >= chan->invitesize) {
|
||||
chan->invitesize += 8;
|
||||
chan->invite =
|
||||
srealloc(chan->invite, sizeof(char *) * chan->invitesize);
|
||||
}
|
||||
chan->invite[chan->invitecount++] = sstrdup(mask);
|
||||
/* Check if the list already exists, if not create it.
|
||||
* Create a new invite and add it to the list.. ~ Viper */
|
||||
if (!chan->invites)
|
||||
chan->invites = list_create();
|
||||
invite = entry_add(chan->invites, mask);
|
||||
|
||||
if (!invite)
|
||||
fatal("Creating new exception entry failed");
|
||||
|
||||
if (debug)
|
||||
alog("debug: Added invite %s to channel %s", mask, chan->name);
|
||||
@@ -1621,7 +1621,6 @@ Channel *chan_create(char *chan, time_t ts)
|
||||
void chan_delete(Channel * c)
|
||||
{
|
||||
BanData *bd, *next;
|
||||
int i;
|
||||
|
||||
if (debug)
|
||||
alog("debug: Deleting channel %s", c->name);
|
||||
@@ -1650,37 +1649,26 @@ void chan_delete(Channel * c)
|
||||
free(c->redirect);
|
||||
}
|
||||
|
||||
for (i = 0; i < c->bancount; ++i) {
|
||||
if (c->bans[i])
|
||||
free(c->bans[i]);
|
||||
else
|
||||
alog("channel: BUG freeing %s: bans[%d] is NULL!", c->name, i);
|
||||
if (c->bans && c->bans->count) {
|
||||
while (c->bans->entries) {
|
||||
entry_delete(c->bans, c->bans->entries);
|
||||
}
|
||||
}
|
||||
if (c->bansize)
|
||||
free(c->bans);
|
||||
|
||||
if (ircd->except) {
|
||||
for (i = 0; i < c->exceptcount; ++i) {
|
||||
if (c->excepts[i])
|
||||
free(c->excepts[i]);
|
||||
else
|
||||
alog("channel: BUG freeing %s: excepts[%d] is NULL!",
|
||||
c->name, i);
|
||||
if (c->excepts && c->excepts->count) {
|
||||
while (c->excepts->entries) {
|
||||
entry_delete(c->excepts, c->excepts->entries);
|
||||
}
|
||||
}
|
||||
if (c->exceptsize)
|
||||
free(c->excepts);
|
||||
}
|
||||
|
||||
if (ircd->invitemode) {
|
||||
for (i = 0; i < c->invitecount; ++i) {
|
||||
if (c->invite[i])
|
||||
free(c->invite[i]);
|
||||
else
|
||||
alog("channel: BUG freeing %s: invite[%d] is NULL!",
|
||||
c->name, i);
|
||||
if (c->invites && c->invites->count) {
|
||||
while (c->invites->entries) {
|
||||
entry_delete(c->invites, c->invites->entries);
|
||||
}
|
||||
}
|
||||
if (c->invitesize)
|
||||
free(c->invite);
|
||||
}
|
||||
|
||||
if (c->next)
|
||||
@@ -1697,28 +1685,20 @@ void chan_delete(Channel * c)
|
||||
|
||||
void del_ban(Channel * chan, char *mask)
|
||||
{
|
||||
char **s = chan->bans;
|
||||
int i = 0;
|
||||
AutoKick *akick;
|
||||
Entry *ban;
|
||||
|
||||
/* Sanity check as it seems some IRCD will just send -b without a mask */
|
||||
if (!mask) {
|
||||
if (!mask || !chan->bans || (chan->bans->count == 0))
|
||||
return;
|
||||
}
|
||||
|
||||
while (i < chan->bancount && strcmp(*s, mask) != 0) {
|
||||
i++;
|
||||
s++;
|
||||
}
|
||||
ban = elist_find_mask(chan->bans, mask);
|
||||
|
||||
if (i < chan->bancount) {
|
||||
chan->bancount--;
|
||||
if (i < chan->bancount)
|
||||
memmove(s, s + 1, sizeof(char *) * (chan->bancount - i));
|
||||
if (ban) {
|
||||
entry_delete(chan->bans, ban);
|
||||
|
||||
if (debug)
|
||||
alog("debug: Deleted ban %s from channel %s", mask,
|
||||
chan->name);
|
||||
alog("debug: Deleted ban %s from channel %s", mask, chan->name);
|
||||
}
|
||||
|
||||
if (chan->ci && (akick = is_stuck(chan->ci, mask)))
|
||||
@@ -1729,58 +1709,41 @@ void del_ban(Channel * chan, char *mask)
|
||||
|
||||
void del_exception(Channel * chan, char *mask)
|
||||
{
|
||||
int i;
|
||||
int reset = 0;
|
||||
Entry *exception;
|
||||
|
||||
/* Sanity check as it seems some IRCD will just send -e without a mask */
|
||||
if (!mask) {
|
||||
if (!mask|| !chan->excepts || (chan->excepts->count == 0))
|
||||
return;
|
||||
|
||||
exception = elist_find_mask(chan->excepts, mask);
|
||||
|
||||
if (exception) {
|
||||
entry_delete(chan->excepts, exception);
|
||||
|
||||
if (debug)
|
||||
alog("debug: Deleted except %s to channel %s", mask, chan->name);
|
||||
}
|
||||
|
||||
for (i = 0; i < chan->exceptcount; i++) {
|
||||
if ((!reset) && (stricmp(chan->excepts[i], mask) == 0)) {
|
||||
free(chan->excepts[i]);
|
||||
reset = 1;
|
||||
}
|
||||
if (reset)
|
||||
chan->excepts[i] =
|
||||
(i == chan->exceptcount) ? NULL : chan->excepts[i + 1];
|
||||
}
|
||||
|
||||
if (reset)
|
||||
chan->exceptcount--;
|
||||
|
||||
if (debug)
|
||||
alog("debug: Deleted except %s to channel %s", mask, chan->name);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void del_invite(Channel * chan, char *mask)
|
||||
{
|
||||
int i;
|
||||
int reset = 0;
|
||||
Entry *invite;
|
||||
|
||||
/* Sanity check as it seems some IRCD will just send -I without a mask */
|
||||
if (!mask) {
|
||||
if (!mask || !chan->invites || (chan->invites->count == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < chan->invitecount; i++) {
|
||||
if ((!reset) && (stricmp(chan->invite[i], mask) == 0)) {
|
||||
free(chan->invite[i]);
|
||||
reset = 1;
|
||||
}
|
||||
if (reset)
|
||||
chan->invite[i] =
|
||||
(i == chan->invitecount) ? NULL : chan->invite[i + 1];
|
||||
invite = elist_find_mask(chan->invites, mask);
|
||||
|
||||
if (invite) {
|
||||
entry_delete(chan->invites, invite);
|
||||
|
||||
if (debug)
|
||||
alog("debug: Deleted invite %s to channel %s", mask, chan->name);
|
||||
}
|
||||
|
||||
if (reset)
|
||||
chan->invitecount--;
|
||||
|
||||
if (debug)
|
||||
alog("debug: Deleted invite %s to channel %s", mask, chan->name);
|
||||
}
|
||||
|
||||
|
||||
@@ -1936,3 +1899,414 @@ void restore_unsynced_topics(void)
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/**
|
||||
* This handles creating a new Entry.
|
||||
* This function destroys and free's the given mask as a side effect.
|
||||
* @param mask Host/IP/CIDR mask to convert to an entry
|
||||
* @return Entry struct for the given mask, NULL if creation failed
|
||||
*/
|
||||
Entry *entry_create(char *mask)
|
||||
{
|
||||
Entry *entry;
|
||||
char *nick = NULL, *user, *host, *cidrhost;
|
||||
int do_free;
|
||||
uint32 ip, cidr;
|
||||
|
||||
entry = scalloc(1, sizeof(Entry));
|
||||
entry->type = ENTRYTYPE_NONE;
|
||||
entry->prev = NULL;
|
||||
entry->next = NULL;
|
||||
entry->nick = NULL;
|
||||
entry->user = NULL;
|
||||
entry->host = NULL;
|
||||
entry->mask = sstrdup(mask);
|
||||
|
||||
host = strchr(mask, '@');
|
||||
if (host) {
|
||||
*host++ = '\0';
|
||||
/* If the user is purely a wildcard, ignore it */
|
||||
if (str_is_pure_wildcard(mask))
|
||||
user = NULL;
|
||||
else {
|
||||
|
||||
/* There might be a nick too */
|
||||
user = strchr(mask, '!');
|
||||
if (user) {
|
||||
*user++ = '\0';
|
||||
/* If the nick is purely a wildcard, ignore it */
|
||||
if (str_is_pure_wildcard(mask))
|
||||
nick = NULL;
|
||||
else
|
||||
nick = mask;
|
||||
} else {
|
||||
nick = NULL;
|
||||
user = mask;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* It is possibly an extended ban/invite mask, but we do
|
||||
* not support these at this point.. ~ Viper */
|
||||
/* If there's no user in the mask, assume a pure wildcard */
|
||||
user = NULL;
|
||||
host = mask;
|
||||
}
|
||||
|
||||
if (nick) {
|
||||
entry->nick = sstrdup(nick);
|
||||
/* Check if we have a wildcard user */
|
||||
if (str_is_wildcard(nick))
|
||||
entry->type |= ENTRYTYPE_NICK_WILD;
|
||||
else
|
||||
entry->type |= ENTRYTYPE_NICK;
|
||||
}
|
||||
|
||||
if (user) {
|
||||
entry->user = sstrdup(user);
|
||||
/* Check if we have a wildcard user */
|
||||
if (str_is_wildcard(user))
|
||||
entry->type |= ENTRYTYPE_USER_WILD;
|
||||
else
|
||||
entry->type |= ENTRYTYPE_USER;
|
||||
}
|
||||
|
||||
/* Only check the host if it's not a pure wildcard */
|
||||
if (*host && !str_is_pure_wildcard(host)) {
|
||||
if (ircd->cidrchanbei && str_is_cidr(host, &ip, &cidr, &cidrhost)) {
|
||||
entry->cidr_ip = ip;
|
||||
entry->cidr_mask = cidr;
|
||||
entry->type |= ENTRYTYPE_CIDR4;
|
||||
host = cidrhost;
|
||||
do_free = 1;
|
||||
} else if (ircd->cidrchanbei && strchr(host, '/')) {
|
||||
/* Most IRCd's don't enforce sane bans therefore it is not
|
||||
* so unlikely we will encounter this.
|
||||
* Currently we only support strict CIDR without taking into
|
||||
* account quirks of every single ircd (nef) that ignore everything
|
||||
* after the first /cidr. To add this, sanitaze before sending to
|
||||
* str_is_cidr() as this expects a standard cidr.
|
||||
* Add it to the internal list (so it is included in for example clear)
|
||||
* but do not use if during matching.. ~ Viper */
|
||||
entry->type = ENTRYTYPE_NONE;
|
||||
} else {
|
||||
entry->host = sstrdup(host);
|
||||
if (str_is_wildcard(host))
|
||||
entry->type |= ENTRYTYPE_HOST_WILD;
|
||||
else
|
||||
entry->type |= ENTRYTYPE_HOST;
|
||||
}
|
||||
}
|
||||
free(mask);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create an entry and add it at the beginning of given list.
|
||||
* @param list The List the mask should be added to
|
||||
* @param mask The mask to parse and add to the list
|
||||
* @return Pointer to newly added entry. NULL if it fails.
|
||||
*/
|
||||
Entry *entry_add(EList *list, char *mask)
|
||||
{
|
||||
Entry *e;
|
||||
char *hostmask;
|
||||
|
||||
hostmask = sstrdup(mask);
|
||||
e = entry_create(hostmask);
|
||||
|
||||
if (!e)
|
||||
return NULL;
|
||||
|
||||
e->next = list->entries;
|
||||
e->prev = NULL;
|
||||
|
||||
if (list->entries)
|
||||
list->entries->prev = e;
|
||||
list->entries = e;
|
||||
list->count++;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete the given entry from a given list.
|
||||
* @param list Linked list from which entry needs to be removed.
|
||||
* @param e The entry to be deleted, must be member of list.
|
||||
*/
|
||||
void entry_delete(EList *list, Entry *e)
|
||||
{
|
||||
if (!list || !e)
|
||||
return;
|
||||
|
||||
if (e->next)
|
||||
e->next->prev = e->prev;
|
||||
if (e->prev)
|
||||
e->prev->next = e->next;
|
||||
|
||||
if (list->entries == e)
|
||||
list->entries = e->next;
|
||||
|
||||
if (e->nick)
|
||||
free(e->nick);
|
||||
if (e->user)
|
||||
free(e->user);
|
||||
if (e->host)
|
||||
free(e->host);
|
||||
free(e->mask);
|
||||
free(e);
|
||||
|
||||
list->count--;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create and initialize a new entrylist
|
||||
* @return Pointer to the created EList object
|
||||
**/
|
||||
EList *list_create() {
|
||||
EList *list;
|
||||
|
||||
list = scalloc(1, sizeof(EList));
|
||||
list->entries = NULL;
|
||||
list->count = 0;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Match the given Entry to the given user/host and optional IP addy
|
||||
* @param e Entry struct to match against
|
||||
* @param nick Nick to match against
|
||||
* @param user User to match against
|
||||
* @param host Host to match against
|
||||
* @param ip IP to match against, set to 0 to not match this
|
||||
* @return 1 for a match, 0 for no match
|
||||
*/
|
||||
int entry_match(Entry *e, char *nick, char *user, char *host, uint32 ip)
|
||||
{
|
||||
/* If we don't get an entry, or it s an invalid one, no match ~ Viper*/
|
||||
if (!e || e->type == ENTRYTYPE_NONE)
|
||||
return 0;
|
||||
|
||||
if (ircd->cidrchanbei && (e->type & ENTRYTYPE_CIDR4) &&
|
||||
(!ip || (ip && ((ip & e->cidr_mask) != e->cidr_ip))))
|
||||
return 0;
|
||||
if ((e->type & ENTRYTYPE_NICK) && (stricmp(e->nick, nick) != 0))
|
||||
return 0;
|
||||
if ((e->type & ENTRYTYPE_USER) && (stricmp(e->user, user) != 0))
|
||||
return 0;
|
||||
if ((e->type & ENTRYTYPE_HOST) && (stricmp(e->host, host) != 0))
|
||||
return 0;
|
||||
if ((e->type & ENTRYTYPE_NICK_WILD) && !match_wild_nocase(e->nick, nick))
|
||||
return 0;
|
||||
if ((e->type & ENTRYTYPE_USER_WILD) && !match_wild_nocase(e->user, user))
|
||||
return 0;
|
||||
if ((e->type & ENTRYTYPE_HOST_WILD) && !match_wild_nocase(e->host, host))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the given Entry to the given hostmask and optional IP addy.
|
||||
* @param e Entry struct to match against
|
||||
* @param mask Hostmask to match against
|
||||
* @param ip IP to match against, set to 0 to not match this
|
||||
* @return 1 for a match, 0 for no match
|
||||
*/
|
||||
int entry_match_mask(Entry *e, char *mask, uint32 ip)
|
||||
{
|
||||
char *hostmask, *nick, *user, *host;
|
||||
int res;
|
||||
|
||||
hostmask = sstrdup(mask);
|
||||
|
||||
host = strchr(hostmask, '@');
|
||||
if (host) {
|
||||
*host++ = '\0';
|
||||
user = strchr(hostmask, '!');
|
||||
if (user) {
|
||||
*user++ = '\0';
|
||||
nick = hostmask;
|
||||
} else {
|
||||
nick = NULL;
|
||||
user = hostmask;
|
||||
}
|
||||
} else {
|
||||
nick = NULL;
|
||||
user = NULL;
|
||||
host = hostmask;
|
||||
}
|
||||
|
||||
res = entry_match(e, nick, user, host, ip);
|
||||
|
||||
/* Free the destroyed mask. */
|
||||
free(hostmask);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a nick, user, host, and ip to a list entry
|
||||
* @param e List that should be matched against
|
||||
* @param nick The nick to match
|
||||
* @param user The user to match
|
||||
* @param host The host to match
|
||||
* @param ip The ip to match
|
||||
* @return Returns the first matching entry, if none, NULL is returned.
|
||||
*/
|
||||
Entry *elist_match(EList *list, char *nick, char *user, char *host, uint32 ip)
|
||||
{
|
||||
Entry *e;
|
||||
|
||||
if (!list || !list->entries)
|
||||
return NULL;
|
||||
|
||||
for (e = list->entries; e; e = e->next) {
|
||||
if (entry_match(e, nick, user, host, ip))
|
||||
return e;
|
||||
}
|
||||
|
||||
/* We matched none */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match a mask and ip to a list.
|
||||
* @param list EntryList that should be matched against
|
||||
* @param mask The nick!user@host mask to match
|
||||
* @param ip The ip to match
|
||||
* @return Returns the first matching entry, if none, NULL is returned.
|
||||
*/
|
||||
Entry *elist_match_mask(EList *list, char *mask, uint32 ip)
|
||||
{
|
||||
char *hostmask, *nick, *user, *host;
|
||||
Entry *res;
|
||||
|
||||
if (!list || !list->entries || !mask)
|
||||
return NULL;
|
||||
|
||||
hostmask = sstrdup(mask);
|
||||
|
||||
host = strchr(hostmask, '@');
|
||||
if (host) {
|
||||
*host++ = '\0';
|
||||
user = strchr(hostmask, '!');
|
||||
if (user) {
|
||||
*user++ = '\0';
|
||||
nick = hostmask;
|
||||
} else {
|
||||
nick = NULL;
|
||||
user = hostmask;
|
||||
}
|
||||
} else {
|
||||
nick = NULL;
|
||||
user = NULL;
|
||||
host = hostmask;
|
||||
}
|
||||
|
||||
res = elist_match(list, nick, user, host, ip);
|
||||
|
||||
/* Free the destroyed mask. */
|
||||
free(hostmask);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user matches an entry on a list.
|
||||
* @param list EntryList that should be matched against
|
||||
* @param user The user to match against the entries
|
||||
* @return Returns the first matching entry, if none, NULL is returned.
|
||||
*/
|
||||
Entry *elist_match_user(EList *list, User *u)
|
||||
{
|
||||
Entry *res;
|
||||
char *host;
|
||||
uint32 ip = 0;
|
||||
|
||||
if (!list || !list->entries || !u)
|
||||
return NULL;
|
||||
|
||||
if (u->hostip == NULL) {
|
||||
host = host_resolve(u->host);
|
||||
/* we store the just resolved hostname so we don't
|
||||
* need to do this again */
|
||||
if (host) {
|
||||
u->hostip = sstrdup(host);
|
||||
}
|
||||
} else {
|
||||
host = sstrdup(u->hostip);
|
||||
}
|
||||
|
||||
/* Convert the host to an IP.. */
|
||||
if (host)
|
||||
ip = str_is_ip(host);
|
||||
|
||||
/* Match what we ve got against the lists.. */
|
||||
res = elist_match(list, u->nick, u->username, u->host, ip);
|
||||
if (!res)
|
||||
elist_match(list, u->nick, u->username, u->vhost, ip);
|
||||
|
||||
if (host)
|
||||
free(host);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a entry identical to the given mask..
|
||||
* @param list EntryList that should be matched against
|
||||
* @param mask The *!*@* mask to match
|
||||
* @return Returns the first matching entry, if none, NULL is returned.
|
||||
*/
|
||||
Entry *elist_find_mask(EList *list, char *mask)
|
||||
{
|
||||
Entry *e;
|
||||
|
||||
if (!list || !list->entries || !mask)
|
||||
return NULL;
|
||||
|
||||
for (e = list->entries; e; e = e->next) {
|
||||
if (!stricmp(e->mask, mask))
|
||||
return e;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total memory use of an entrylit.
|
||||
* @param list The list we should estimate the mem use of.
|
||||
* @return Returns the memory useage of the given list.
|
||||
*/
|
||||
long get_memuse(EList *list) {
|
||||
Entry *e;
|
||||
long mem = 0;
|
||||
|
||||
if (!list)
|
||||
return 0;
|
||||
|
||||
mem += sizeof(EList *);
|
||||
mem += sizeof(Entry *) * list->count;
|
||||
if (list->entries) {
|
||||
for (e = list->entries; e; e = e->next) {
|
||||
if (e->nick)
|
||||
mem += strlen(e->nick) + 1;
|
||||
if (e->user)
|
||||
mem += strlen(e->user) + 1;
|
||||
if (e->host)
|
||||
mem += strlen(e->host) + 1;
|
||||
if (e->mask)
|
||||
mem += strlen(e->mask) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
+13
-12
@@ -2570,29 +2570,30 @@ AutoKick *is_stuck(ChannelInfo * ci, char *mask)
|
||||
|
||||
void stick_mask(ChannelInfo * ci, AutoKick * akick)
|
||||
{
|
||||
int i;
|
||||
char *av[2];
|
||||
Entry *ban;
|
||||
|
||||
if (!ci) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ci->c->bancount; i++) {
|
||||
/* If akick is already covered by a wider ban.
|
||||
Example: c->bans[i] = *!*@*.org and akick->u.mask = *!*@*.epona.org */
|
||||
if (match_wild_nocase(ci->c->bans[i], akick->u.mask))
|
||||
return;
|
||||
|
||||
if (ircd->reversekickcheck) {
|
||||
/* If akick is wider than a ban already in place.
|
||||
Example: c->bans[i] = *!*@irc.epona.org and akick->u.mask = *!*@*.epona.org */
|
||||
if (match_wild_nocase(akick->u.mask, ci->c->bans[i]))
|
||||
if (ci->c->bans && ci->c->bans->entries != 0) {
|
||||
for (ban = ci->c->bans->entries; ban; ban = ban->next) {
|
||||
/* If akick is already covered by a wider ban.
|
||||
Example: c->bans[i] = *!*@*.org and akick->u.mask = *!*@*.epona.org */
|
||||
if (entry_match_mask(ban, sstrdup(akick->u.mask), 0))
|
||||
return;
|
||||
|
||||
if (ircd->reversekickcheck) {
|
||||
/* If akick is wider than a ban already in place.
|
||||
Example: c->bans[i] = *!*@irc.epona.org and akick->u.mask = *!*@*.epona.org */
|
||||
if (match_wild_nocase(akick->u.mask, ban->mask))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Falling there means set the ban */
|
||||
|
||||
av[0] = sstrdup("+b");
|
||||
av[1] = akick->u.mask;
|
||||
anope_cmd_mode(whosends(ci), ci->c->name, "+b %s", akick->u.mask);
|
||||
|
||||
+35
-46
@@ -85,65 +85,54 @@ int do_clear(User * u)
|
||||
notice_lang(s_ChanServ, u, PERMISSION_DENIED);
|
||||
} else if (stricmp(what, "bans") == 0) {
|
||||
char *av[2];
|
||||
int i;
|
||||
Entry *ban, *next;
|
||||
|
||||
/* Save original ban info */
|
||||
int count = c->bancount;
|
||||
char **bans = scalloc(sizeof(char *) * count, 1);
|
||||
for (i = 0; i < count; i++)
|
||||
bans[i] = sstrdup(c->bans[i]);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
av[0] = sstrdup("-b");
|
||||
av[1] = bans[i];
|
||||
anope_cmd_mode(whosends(ci), chan, "%s %s", av[0], av[1]);
|
||||
chan_set_modes(whosends(ci), c, 2, av, 0);
|
||||
free(av[1]);
|
||||
free(av[0]);
|
||||
if (c->bans && c->bans->count) {
|
||||
for (ban = c->bans->entries; ban; ban = next) {
|
||||
next = ban->next;
|
||||
av[0] = sstrdup("-b");
|
||||
av[1] = sstrdup(ban->mask);
|
||||
anope_cmd_mode(whosends(ci), chan, "-b %s", ban->mask);
|
||||
chan_set_modes(whosends(ci), c, 2, av, 0);
|
||||
free(av[0]);
|
||||
free(av[1]);
|
||||
}
|
||||
}
|
||||
|
||||
notice_lang(s_ChanServ, u, CHAN_CLEARED_BANS, chan);
|
||||
free(bans);
|
||||
} else if (ircd->except && stricmp(what, "excepts") == 0) {
|
||||
char *av[2];
|
||||
int i;
|
||||
Entry *except, *next;
|
||||
|
||||
/* Save original except info */
|
||||
int count = c->exceptcount;
|
||||
char **excepts = scalloc(sizeof(char *) * count, 1);
|
||||
for (i = 0; i < count; i++)
|
||||
excepts[i] = sstrdup(c->excepts[i]);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
av[0] = sstrdup("-e");
|
||||
av[1] = excepts[i];
|
||||
anope_cmd_mode(whosends(ci), chan, "%s %s", av[0], av[1]);
|
||||
chan_set_modes(whosends(ci), c, 2, av, 0);
|
||||
free(av[1]);
|
||||
free(av[0]);
|
||||
if (c->excepts && c->excepts->count) {
|
||||
for (except = c->excepts->entries; except; except = next) {
|
||||
next = except->next;
|
||||
av[0] = sstrdup("-e");
|
||||
av[1] = sstrdup(except->mask);
|
||||
anope_cmd_mode(whosends(ci), chan, "-e %s", except->mask);
|
||||
chan_set_modes(whosends(ci), c, 2, av, 0);
|
||||
free(av[0]);
|
||||
free(av[1]);
|
||||
}
|
||||
}
|
||||
notice_lang(s_ChanServ, u, CHAN_CLEARED_EXCEPTS, chan);
|
||||
free(excepts);
|
||||
|
||||
} else if (ircd->invitemode && stricmp(what, "invites") == 0) {
|
||||
char *av[2];
|
||||
int i;
|
||||
Entry *invite, *next;
|
||||
|
||||
/* Save original except info */
|
||||
int count = c->invitecount;
|
||||
char **invites = scalloc(sizeof(char *) * count, 1);
|
||||
for (i = 0; i < count; i++)
|
||||
invites[i] = sstrdup(c->invite[i]);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
av[0] = sstrdup("-I");
|
||||
av[1] = invites[i];
|
||||
anope_cmd_mode(whosends(ci), chan, "%s %s", av[0], av[1]);
|
||||
chan_set_modes(whosends(ci), c, 2, av, 0);
|
||||
free(av[1]);
|
||||
free(av[0]);
|
||||
if (c->invites && c->invites->count) {
|
||||
for (invite = c->invites->entries; invite; invite = next) {
|
||||
next = invite->next;
|
||||
av[0] = sstrdup("-I");
|
||||
av[1] = sstrdup(invite->mask);
|
||||
anope_cmd_mode(whosends(ci), chan, "-I %s", invite->mask);
|
||||
chan_set_modes(whosends(ci), c, 2, av, 0);
|
||||
free(av[0]);
|
||||
free(av[1]);
|
||||
}
|
||||
}
|
||||
notice_lang(s_ChanServ, u, CHAN_CLEARED_INVITES, chan);
|
||||
free(invites);
|
||||
|
||||
} else if (stricmp(what, "modes") == 0) {
|
||||
char *argv[2];
|
||||
@@ -189,7 +178,7 @@ int do_clear(User * u)
|
||||
}
|
||||
check_modes(c);
|
||||
}
|
||||
|
||||
|
||||
notice_lang(s_ChanServ, u, CHAN_CLEARED_MODES, chan);
|
||||
} else if (stricmp(what, "ops") == 0) {
|
||||
char *av[4];
|
||||
|
||||
+25
-54
@@ -75,13 +75,8 @@ int do_clearmodes(User * u)
|
||||
char *chan = strtok(NULL, " ");
|
||||
Channel *c;
|
||||
int all = 0;
|
||||
int count; /* For saving ban info */
|
||||
char **bans; /* For saving ban info */
|
||||
int exceptcount; /* For saving except info */
|
||||
char **excepts; /* For saving except info */
|
||||
int invitecount; /* For saving invite info */
|
||||
char **invites; /* For saving invite info */
|
||||
struct c_userlist *cu, *next;
|
||||
Entry *entry, *nexte;
|
||||
|
||||
if (!chan) {
|
||||
syntax_error(s_OperServ, u, "CLEARMODES", OPER_CLEARMODES_SYNTAX);
|
||||
@@ -286,67 +281,43 @@ int do_clearmodes(User * u)
|
||||
}
|
||||
|
||||
/* Clear bans */
|
||||
count = c->bancount;
|
||||
bans = scalloc(sizeof(char *) * count, 1);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
bans[i] = sstrdup(c->bans[i]);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
argv[0] = sstrdup("-b");
|
||||
argv[1] = bans[i];
|
||||
anope_cmd_mode(s_OperServ, c->name, "-b %s", argv[1]);
|
||||
chan_set_modes(s_OperServ, c, 2, argv, 0);
|
||||
free(argv[1]);
|
||||
free(argv[0]);
|
||||
if (c->bans && c->bans->count) {
|
||||
for (entry = c->bans->entries; entry; entry = nexte) {
|
||||
nexte = entry->next;
|
||||
argv[0] = sstrdup("-b");
|
||||
argv[1] = sstrdup(entry->mask);
|
||||
anope_cmd_mode(s_OperServ, c->name, "-b %s", entry->mask);
|
||||
chan_set_modes(s_OperServ, c, 2, argv, 0);
|
||||
free(argv[0]);
|
||||
free(argv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
free(bans);
|
||||
|
||||
excepts = NULL;
|
||||
|
||||
if (ircd->except) {
|
||||
/* Clear excepts */
|
||||
exceptcount = c->exceptcount;
|
||||
excepts = scalloc(sizeof(char *) * exceptcount, 1);
|
||||
|
||||
for (i = 0; i < exceptcount; i++)
|
||||
excepts[i] = sstrdup(c->excepts[i]);
|
||||
|
||||
for (i = 0; i < exceptcount; i++) {
|
||||
/* Clear excepts */
|
||||
if (ircd->except && c->excepts && c->excepts->count) {
|
||||
for (entry = c->excepts->entries; entry; entry = nexte) {
|
||||
nexte = entry->next;
|
||||
argv[0] = sstrdup("-e");
|
||||
argv[1] = excepts[i];
|
||||
anope_cmd_mode(s_OperServ, c->name, "-e %s", argv[1]);
|
||||
argv[1] = sstrdup(entry->mask);
|
||||
anope_cmd_mode(s_OperServ, c->name, "-e %s", entry->mask);
|
||||
chan_set_modes(s_OperServ, c, 2, argv, 0);
|
||||
free(argv[1]);
|
||||
free(argv[0]);
|
||||
}
|
||||
|
||||
if (excepts) {
|
||||
free(excepts);
|
||||
free(argv[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (ircd->invitemode) {
|
||||
/* Clear invites */
|
||||
invitecount = c->invitecount;
|
||||
invites = scalloc(sizeof(char *) * invitecount, 1);
|
||||
|
||||
for (i = 0; i < invitecount; i++)
|
||||
invites[i] = sstrdup(c->invite[i]);
|
||||
|
||||
for (i = 0; i < invitecount; i++) {
|
||||
/* Clear invites */
|
||||
if (ircd->invitemode && c->invites && c->invites->count) {
|
||||
for (entry = c->invites->entries; entry; entry = nexte) {
|
||||
nexte = entry->next;
|
||||
argv[0] = sstrdup("-I");
|
||||
argv[1] = invites[i];
|
||||
anope_cmd_mode(s_OperServ, c->name, "-I %s", argv[1]);
|
||||
argv[1] = sstrdup(entry->mask);
|
||||
anope_cmd_mode(s_OperServ, c->name, "-I %s", entry->mask);
|
||||
chan_set_modes(s_OperServ, c, 2, argv, 0);
|
||||
free(argv[1]);
|
||||
free(argv[0]);
|
||||
free(argv[1]);
|
||||
}
|
||||
|
||||
free(invites);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (all) {
|
||||
|
||||
+168
@@ -1492,4 +1492,172 @@ int SupportedWindowsVersion(void)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*************************************************************************/
|
||||
/* This 2 functions were originally found in Bahamut */
|
||||
|
||||
/**
|
||||
* Turn a cidr value into a netmask
|
||||
* @param cidr CIDR value
|
||||
* @return Netmask value
|
||||
*/
|
||||
uint32 cidr_to_netmask(uint16 cidr)
|
||||
{
|
||||
if (cidr == 0)
|
||||
return 0;
|
||||
|
||||
return (0xFFFFFFFF - (1 << (32 - cidr)) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a netmask into a cidr value
|
||||
* @param mask Netmask
|
||||
* @return CIDR value
|
||||
*/
|
||||
uint16 netmask_to_cidr(uint32 mask)
|
||||
{
|
||||
int tmp = 0;
|
||||
|
||||
while (!(mask & (1 << tmp)) && (tmp < 32))
|
||||
tmp++;
|
||||
|
||||
return (32 - tmp);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/**
|
||||
* Check if the given string is some sort of wildcard
|
||||
* @param str String to check
|
||||
* @return 1 for wildcard, 0 for anything else
|
||||
*/
|
||||
int str_is_wildcard(const char *str)
|
||||
{
|
||||
while (*str) {
|
||||
if ((*str == '*') || (*str == '?'))
|
||||
return 1;
|
||||
str++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given string is a pure wildcard
|
||||
* @param str String to check
|
||||
* @return 1 for pure wildcard, 0 for anything else
|
||||
*/
|
||||
int str_is_pure_wildcard(const char *str)
|
||||
{
|
||||
while (*str) {
|
||||
if (*str != '*')
|
||||
return 0;
|
||||
str++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/**
|
||||
* Check if the given string is an IP, and return the IP.
|
||||
* @param str String to check
|
||||
* @return The IP, if one found. 0 if none.
|
||||
*/
|
||||
uint32 str_is_ip(char *str)
|
||||
{
|
||||
int i;
|
||||
int octets[4] = { -1, -1, -1, -1 };
|
||||
char *s = str;
|
||||
uint32 ip;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
octets[i] = strtol(s, &s, 10);
|
||||
/* Bail out if the octet is invalid or wrongly terminated */
|
||||
if ((octets[i] < 0) || (octets[i] > 255)
|
||||
|| ((i < 3) && (*s != '.')))
|
||||
return 0;
|
||||
if (i < 3)
|
||||
s++;
|
||||
}
|
||||
|
||||
/* Fill the IP - the dirty way */
|
||||
ip = octets[3];
|
||||
ip += octets[2] * 256;
|
||||
ip += octets[1] * 65536;
|
||||
ip += octets[0] * 16777216;
|
||||
|
||||
return ip;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/**
|
||||
* Check if the given string is an IP or CIDR mask, and fill the given
|
||||
* ip/cidr params if so.
|
||||
* @param str String to check
|
||||
* @param ip The ipmask to fill when a CIDR is found
|
||||
* @param mask The CIDR mask to fill when a CIDR is found
|
||||
* @param host Displayed host
|
||||
* @return 1 for IP/CIDR, 0 for anything else
|
||||
*/
|
||||
int str_is_cidr(char *str, uint32 * ip, uint32 * mask, char **host)
|
||||
{
|
||||
int i;
|
||||
int octets[4] = { -1, -1, -1, -1 };
|
||||
char *s = str;
|
||||
char buf[512];
|
||||
uint16 cidr;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
octets[i] = strtol(s, &s, 10);
|
||||
/* Bail out if the octet is invalid or wrongly terminated */
|
||||
if ((octets[i] < 0) || (octets[i] > 255)
|
||||
|| ((i < 3) && (*s != '.')))
|
||||
return 0;
|
||||
if (i < 3)
|
||||
s++;
|
||||
}
|
||||
|
||||
/* Fill the IP - the dirty way */
|
||||
*ip = octets[3];
|
||||
*ip += octets[2] * 256;
|
||||
*ip += octets[1] * 65536;
|
||||
*ip += octets[0] * 16777216;
|
||||
|
||||
if (*s == '/') {
|
||||
s++;
|
||||
/* There's a CIDR mask here! */
|
||||
cidr = strtol(s, &s, 10);
|
||||
/* Bail out if the CIDR is invalid or the string isn't done yet */
|
||||
if ((cidr > 32) || (*s))
|
||||
return 0;
|
||||
} else {
|
||||
/* No CIDR mask here - use 32 so the whole ip will be matched */
|
||||
cidr = 32;
|
||||
}
|
||||
|
||||
*mask = cidr_to_netmask(cidr);
|
||||
/* Apply the mask to avoid 255.255.255.255/8 bans */
|
||||
*ip &= *mask;
|
||||
|
||||
/* Refill the octets to fill the host */
|
||||
octets[0] = (*ip & 0xFF000000) / 16777216;
|
||||
octets[1] = (*ip & 0x00FF0000) / 65536;
|
||||
octets[2] = (*ip & 0x0000FF00) / 256;
|
||||
octets[3] = (*ip & 0x000000FF);
|
||||
|
||||
if (cidr == 32)
|
||||
snprintf(buf, 512, "%d.%d.%d.%d", octets[0], octets[1], octets[2],
|
||||
octets[3]);
|
||||
else
|
||||
snprintf(buf, 512, "%d.%d.%d.%d/%d", octets[0], octets[1],
|
||||
octets[2], octets[3], cidr);
|
||||
|
||||
*host = sstrdup(buf);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* EOF */
|
||||
|
||||
@@ -107,6 +107,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
1, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
@@ -634,7 +635,7 @@ void moduleAddIRCDMsgs(void) {
|
||||
|
||||
/* first update the cs protect info about this ircd */
|
||||
updateProtectDetails("PROTECT","PROTECTME","protect","deprotect","AUTOPROTECT","+","-");
|
||||
|
||||
|
||||
/* now add the commands */
|
||||
m = createMessage("401", anope_event_null); addCoreMessage(IRCD,m);
|
||||
m = createMessage("402", anope_event_null); addCoreMessage(IRCD,m);
|
||||
|
||||
@@ -104,6 +104,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
0, /* reports sync state */
|
||||
1, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
@@ -237,7 +238,7 @@ unsigned long umodes[128] = {
|
||||
0, /* y */
|
||||
UMODE_z, /* z */
|
||||
0, 0, 0, /* { | } */
|
||||
0, 0 /* ~ � */
|
||||
0, 0 /* ~ � */
|
||||
};
|
||||
|
||||
|
||||
@@ -741,15 +742,15 @@ int anope_event_tburst(char *source, int ac, char **av)
|
||||
c->topic_time = topic_time;
|
||||
|
||||
record_topic(av[0]);
|
||||
|
||||
|
||||
if (ac > 1 && *av[3])
|
||||
send_event(EVENT_TOPIC_UPDATED, 2, av[0], av[3]);
|
||||
else
|
||||
send_event(EVENT_TOPIC_UPDATED, 2, av[0], "");
|
||||
|
||||
|
||||
if (setter)
|
||||
free(setter);
|
||||
|
||||
|
||||
return MOD_CONT;
|
||||
}
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
0, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
@@ -254,7 +255,7 @@ unsigned long umodes[128] = {
|
||||
0, /* y */
|
||||
0, /* z */
|
||||
0, 0, 0, /* { | } */
|
||||
0, 0 /* ~ � */
|
||||
0, 0 /* ~ � */
|
||||
};
|
||||
|
||||
char myCsmodes[128] = {
|
||||
|
||||
@@ -105,6 +105,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
0, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
@@ -256,7 +257,7 @@ unsigned long umodes[128] = {
|
||||
UMODE_y, /* y */
|
||||
UMODE_z, /* z */
|
||||
0, 0, 0, /* { | } */
|
||||
0, 0 /* ~ ‚ */
|
||||
0, 0 /* ~ � */
|
||||
};
|
||||
|
||||
|
||||
@@ -568,11 +569,11 @@ int anope_event_topic(char *source, int ac, char **av)
|
||||
c->topic_time = topic_time;
|
||||
|
||||
record_topic(av[0]);
|
||||
|
||||
|
||||
if (ac > 1 && *av[1])
|
||||
send_event(EVENT_TOPIC_UPDATED, 2, av[0], av[1]);
|
||||
else
|
||||
send_event(EVENT_TOPIC_UPDATED, 2, av[0], "");
|
||||
send_event(EVENT_TOPIC_UPDATED, 2, av[0], "");
|
||||
}
|
||||
return MOD_CONT;
|
||||
}
|
||||
|
||||
@@ -124,6 +124,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
0, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
|
||||
@@ -124,6 +124,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
0, /* reports sync state */
|
||||
1, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
|
||||
@@ -103,6 +103,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
1, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
@@ -322,7 +323,7 @@ unsigned long umodes[128] = {
|
||||
0, /* y */
|
||||
0, /* z */
|
||||
0, 0, 0, /* { | } */
|
||||
0, 0 /* ~ � */
|
||||
0, 0 /* ~ � */
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
1, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
@@ -264,7 +265,7 @@ unsigned long umodes[128] = {
|
||||
0, /* y */
|
||||
0, /* z */
|
||||
0, 0, 0, /* { | } */
|
||||
0, 0 /* ~ � */
|
||||
0, 0 /* ~ � */
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
0, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
@@ -192,7 +193,7 @@ unsigned long umodes[128] = {
|
||||
UMODE_y, /* y */
|
||||
UMODE_z, /* z */
|
||||
0, 0, 0, /* { | } */
|
||||
0, 0 /* ~ � */
|
||||
0, 0 /* ~ � */
|
||||
};
|
||||
|
||||
char myCsmodes[128] = {
|
||||
|
||||
@@ -105,6 +105,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
1, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
@@ -192,7 +193,7 @@ unsigned long umodes[128] = {
|
||||
0, /* y */
|
||||
0, /* z */
|
||||
0, 0, 0, /* { | } */
|
||||
0, 0 /* ~ � */
|
||||
0, 0 /* ~ � */
|
||||
};
|
||||
|
||||
char myCsmodes[128] = {
|
||||
|
||||
@@ -105,6 +105,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
0, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
@@ -236,7 +237,7 @@ unsigned long umodes[128] = {
|
||||
0, /* y */
|
||||
0, /* z */
|
||||
0, 0, 0, /* { | } */
|
||||
0, 0 /* ~ � */
|
||||
0, 0 /* ~ � */
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -106,6 +106,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
1, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
@@ -238,7 +239,7 @@ unsigned long umodes[128] = {
|
||||
0, /* y */
|
||||
0, /* z */
|
||||
0, 0, 0, /* { | } */
|
||||
0, 0 /* ~ � */
|
||||
0, 0 /* ~ � */
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
1, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
@@ -269,7 +270,7 @@ unsigned long umodes[128] = {
|
||||
UMODE_y, /* y */
|
||||
UMODE_z, /* z */
|
||||
0, 0, 0, /* { | } */
|
||||
0, 0 /* ~ � */
|
||||
0, 0 /* ~ � */
|
||||
};
|
||||
|
||||
char myCsmodes[128] = {
|
||||
|
||||
@@ -105,6 +105,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
0, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
|
||||
@@ -105,6 +105,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
1, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
@@ -285,7 +286,7 @@ unsigned long umodes[128] = {
|
||||
0, /* y */
|
||||
0, /* z */
|
||||
0, 0, 0, /* { | } */
|
||||
0, 0 /* ~ � */
|
||||
0, 0 /* ~ � */
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
0, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
|
||||
@@ -107,6 +107,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
1, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
@@ -197,7 +198,7 @@ unsigned long umodes[128] = {
|
||||
0, /* y */
|
||||
UMODE_z, /* z */
|
||||
0, 0, 0, /* { | } */
|
||||
0, 0 /* ~ � */
|
||||
0, 0 /* ~ � */
|
||||
};
|
||||
|
||||
char myCsmodes[128] = {
|
||||
|
||||
@@ -106,6 +106,7 @@ IRCDVar myIrcd[] = {
|
||||
0, /* p10 */
|
||||
NULL, /* character set */
|
||||
1, /* reports sync state */
|
||||
0, /* CIDR channelbans */
|
||||
}
|
||||
,
|
||||
{NULL}
|
||||
|
||||
+9
-41
@@ -506,7 +506,7 @@ User *do_nick(const char *source, char *nick, char *username, char *host,
|
||||
if (!strcmp(vhost, "*")) {
|
||||
vhost = NULL;
|
||||
if (debug)
|
||||
alog("debug: new user with no vhost in NICK command: %s", nick);
|
||||
alog("debug: new user�with no vhost in NICK command: %s", nick);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -921,30 +921,13 @@ int is_oper(User * user)
|
||||
/* Is the given user ban-excepted? */
|
||||
int is_excepted(ChannelInfo * ci, User * user)
|
||||
{
|
||||
int count, i;
|
||||
int isexcepted = 0;
|
||||
char **excepts;
|
||||
|
||||
if (!ci->c)
|
||||
if (!ci->c || !ircd->except)
|
||||
return 0;
|
||||
|
||||
if (!ircd->except) {
|
||||
return 0;
|
||||
}
|
||||
if (elist_match_user(ci->c->excepts, user))
|
||||
return 1;
|
||||
|
||||
count = ci->c->exceptcount;
|
||||
excepts = scalloc(sizeof(char *) * count, 1);
|
||||
memcpy(excepts, ci->c->excepts, sizeof(char *) * count);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (match_usermask(excepts[i], user)
|
||||
|| match_userip(excepts[i], user, user->hostip)) {
|
||||
isexcepted = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(excepts);
|
||||
return isexcepted;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
@@ -952,28 +935,13 @@ int is_excepted(ChannelInfo * ci, User * user)
|
||||
/* Is the given MASK ban-excepted? */
|
||||
int is_excepted_mask(ChannelInfo * ci, char *mask)
|
||||
{
|
||||
int count, i;
|
||||
int isexcepted = 0;
|
||||
char **excepts;
|
||||
|
||||
if (!ci->c)
|
||||
if (!ci->c || !ircd->except)
|
||||
return 0;
|
||||
|
||||
if (!ircd->except) {
|
||||
return 0;
|
||||
}
|
||||
if (elist_match_mask(ci->c->excepts, mask, 0))
|
||||
return 1;
|
||||
|
||||
count = ci->c->exceptcount;
|
||||
excepts = scalloc(sizeof(char *) * count, 1);
|
||||
memcpy(excepts, ci->c->excepts, sizeof(char *) * count);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (match_wild_nocase(excepts[i], mask)) {
|
||||
isexcepted = 1;
|
||||
}
|
||||
}
|
||||
free(excepts);
|
||||
return isexcepted;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+5
-1
@@ -9,10 +9,14 @@ VERSION_MAJOR="1"
|
||||
VERSION_MINOR="7"
|
||||
VERSION_PATCH="21"
|
||||
VERSION_EXTRA="-svn"
|
||||
VERSION_BUILD="1425"
|
||||
VERSION_BUILD="1426"
|
||||
|
||||
# $Log$
|
||||
#
|
||||
# BUILD : 1.7.21 (1426)
|
||||
# BUGS : 876
|
||||
# NOTES : We now support CIDR in channel ban/invite/except lists. Introduces new CIDR capable generic lists system.
|
||||
#
|
||||
# BUILD : 1.7.21 (1425)
|
||||
# BUGS : 873
|
||||
# NOTES : We now show config variables in the help.
|
||||
|
||||
Reference in New Issue
Block a user