Handle PREFIX defined in ISUPPORT

ISUPPORT is a de facto standard extension to IRC.
Available prefixes and usermodes are handled at the connection
level (link_level struct).
This commit is contained in:
Pierre-Louis Bonicoli 2014-09-26 14:59:57 +02:00
parent e88d7da760
commit 73483ff088
2 changed files with 143 additions and 111 deletions

246
src/irc.c
View File

@ -30,8 +30,8 @@ extern bip_t *_bip;
static int irc_join(struct link_server *server, struct line *line); static int irc_join(struct link_server *server, struct line *line);
static int irc_part(struct link_server *server, struct line *line); static int irc_part(struct link_server *server, struct line *line);
static int irc_mode(struct link_server *server, struct line *line); static int irc_mode(struct link_server *server, struct line *line);
static int irc_mode_channel(struct channel *channel, struct line *line, static int irc_mode_channel(struct link_server *s, struct channel *channel,
const char* mode, int add, int cur_arg); struct line *line, const char* mode, int add, int cur_arg);
static int irc_kick(struct link_server *server, struct line *line); static int irc_kick(struct link_server *server, struct line *line);
static int irc_privmsg(struct link_server *server, struct line *line); static int irc_privmsg(struct link_server *server, struct line *line);
static int irc_notice(struct link_server *server, struct line *line); static int irc_notice(struct link_server *server, struct line *line);
@ -49,6 +49,10 @@ void irc_server_shutdown(struct link_server *s);
static int origin_is_me(struct line *l, struct link_server *server); static int origin_is_me(struct line *l, struct link_server *server);
static void ls_set_nick(struct link_server *ircs, char *nick); static void ls_set_nick(struct link_server *ircs, char *nick);
static void server_set_chanmodes(struct link_server *l, const char *chanmodes); static void server_set_chanmodes(struct link_server *l, const char *chanmodes);
static void server_set_prefix(struct link_server *l, const char *prefix);
static void server_init_modes(struct link_server *s);
static int get_index(const char* str, char car);
static int fls(int v);
#ifdef HAVE_OIDENTD #ifdef HAVE_OIDENTD
#define OIDENTD_FILENAME ".oidentd.conf" #define OIDENTD_FILENAME ".oidentd.conf"
@ -104,7 +108,7 @@ char *nick_from_ircmask(const char *mask)
#define NAMESIZE 256 #define NAMESIZE 256
list_t *channel_name_list(struct channel *c) list_t *channel_name_list(struct link_server *server, struct channel *c)
{ {
list_t *ret; list_t *ret;
hash_iterator_t hi; hash_iterator_t hi;
@ -130,13 +134,13 @@ list_t *channel_name_list(struct channel *c)
strcat(str, " "); strcat(str, " ");
len++; len++;
} }
if (ovmask & NICKOP)
strcat(str, "@"); // prepend symbol corresponding to the usermode
else if (ovmask & NICKHALFOP) int msb;
strcat(str, "%"); if ((msb = fls(ovmask))) {
else if (ovmask & NICKVOICED) str[len] = server->prefixes[msb - 1];
strcat(str, "+"); len++;
len++; }
strcat(str, nick); strcat(str, nick);
len += strlen(nick); len += strlen(nick);
@ -427,6 +431,8 @@ int irc_dispatch_server(bip_t *bip, struct link_server *server,
irc_line_drop(line, i); irc_line_drop(line, i);
else if (!strncmp(irc_line_elem(line, i), "CHANMODES=", 10)) else if (!strncmp(irc_line_elem(line, i), "CHANMODES=", 10))
server_set_chanmodes(server, irc_line_elem(line, i) + 10); server_set_chanmodes(server, irc_line_elem(line, i) + 10);
else if (!strncmp(irc_line_elem(line, i), "PREFIX=(", 8))
server_set_prefix(server, irc_line_elem(line, i) + 7);
} }
} }
if (irc_line_elem_equals(line, 0, "NOTICE")) { if (irc_line_elem_equals(line, 0, "NOTICE")) {
@ -539,7 +545,7 @@ static void irc_send_join(struct link_client *ic, struct channel *chan)
WRITE_LINE4(CONN(ic), P_SERV, "333", LINK(ic)->l_server->nick, WRITE_LINE4(CONN(ic), P_SERV, "333", LINK(ic)->l_server->nick,
chan->name, chan->creator, chan->create_ts); chan->name, chan->creator, chan->create_ts);
list_t *name_list = channel_name_list(chan); list_t *name_list = channel_name_list(LINK(ic)->l_server, chan);
char *s; char *s;
while ((s = list_remove_first(name_list))) { while ((s = list_remove_first(name_list))) {
char tmptype[2]; char tmptype[2];
@ -1389,33 +1395,14 @@ static int irc_353(struct link_server *server, struct line *line)
names = irc_line_elem(line, 4); names = irc_line_elem(line, 4);
int index;
while (*names) { while (*names) {
long int ovmask = 0; long int ovmask = 0;
int flagchars = 1;
/* some ircds (e.g. unreal) may display several flags for the /* some ircds (e.g. unreal) may display several flags for the
same nick */ same nick */
while (flagchars) { while ((index = get_index(server->prefixes, *names))) {
switch (*names) { ovmask |= 1 << index;
case '@': names++;
names++;
ovmask |= NICKOP;
break;
case '+':
names++;
ovmask |= NICKVOICED;
break;
case '%': /* unrealircd and others: halfop */
names++;
ovmask |= NICKHALFOP;
break;
case '&': /* unrealircd: protect */
case '~': /* unrealircd: owner */
names++;
break;
default:
flagchars = 0;
break;
}
} }
eon = names; eon = names;
while (*eon && *eon != ' ') while (*eon && *eon != ' ')
@ -1644,33 +1631,32 @@ static int irc_mode(struct link_server *server, struct line *line)
else if (*mode == '+') else if (*mode == '+')
add = 1; add = 1;
else { else {
int i; int i = 0;
char *str; char *str = 0;
array_each(&server->chanmodes, i, str) {
if (strchr(str, *mode)) { // Check if mode is known: first user modes then
// Types A & B always take a parameter // server modes
// Type C take a parameter only when set if (!(str = strchr(server->usermodes, *mode))) {
if (i <= 1 || (i == 2 && add)) { array_each(&server->chanmodes, i, str) {
if (!irc_line_includes(line, cur_arg + 3)) { if ((str = strchr(str, *mode)))
ret = ERR_PROTOCOL; break;
} else {
ret = irc_mode_channel(channel, line, mode, add, cur_arg);
cur_arg++;
}
} else {
ret = irc_mode_channel(channel, line, mode, add, cur_arg);
}
break;
} }
} }
// Prefix if (str) {
if (strchr("ovh", *mode)) { // Usermodes, types A & B always take a parameter
if (!irc_line_includes(line, cur_arg + 3)) { // Type C take a parameter only when set
ret = ERR_PROTOCOL; if (i <= 1 || (i == 2 && add)) {
if (!irc_line_includes(line, cur_arg + 3)) {
return ERR_PROTOCOL;
} else {
ret = irc_mode_channel(server, channel, line, mode,
add, cur_arg);
cur_arg++;
}
} else { } else {
ret = irc_mode_channel(channel, line, mode, add, cur_arg); ret = irc_mode_channel(server, channel, line, mode, add,
cur_arg++; cur_arg);
} }
} }
} }
@ -1680,62 +1666,35 @@ static int irc_mode(struct link_server *server, struct line *line)
return OK_COPY; return OK_COPY;
} }
static int irc_mode_channel(struct channel *channel, struct line *line, static int irc_mode_channel(struct link_server *s, struct channel *channel,
const char* mode, int add, int cur_arg) struct line *line, const char* mode, int add, int cur_arg)
{ {
const char *nick; const char *nick;
long int ovmask; long int ovmask;
int index;
switch (*mode) { if (*mode == 'k') {
case 'k': if (add) {
if (add) { channel->key = bip_strdup(
channel->key = bip_strdup( irc_line_elem(line, cur_arg + 3));
irc_line_elem(line, cur_arg + 3)); } else {
} else { if (channel->key) {
if (channel->key) { free(channel->key);
free(channel->key); channel->key = NULL;
channel->key = NULL;
}
} }
break; }
case 'o': } else if ((index = get_index(s->usermodes, *mode))) {
nick = irc_line_elem(line, cur_arg + 3); nick = irc_line_elem(line, cur_arg + 3);
if (!hash_includes(&channel->ovmasks, nick)) if (!hash_includes(&channel->ovmasks, nick))
return ERR_PROTOCOL; return ERR_PROTOCOL;
ovmask = (long int)hash_remove(&channel->ovmasks, nick); ovmask = (long int)hash_remove(&channel->ovmasks, nick);
if (add)
ovmask |= NICKOP;
else
ovmask &= ~NICKOP;
hash_insert(&channel->ovmasks, nick, (void *)ovmask);
break;
case 'h':
nick = irc_line_elem(line, cur_arg + 3);
if (!hash_includes(&channel->ovmasks, nick)) if (add)
return ERR_PROTOCOL; ovmask |= 1 << index;
else
ovmask = (long int)hash_remove(&channel->ovmasks, nick); ovmask &= ~(1 << index);
if (add) hash_insert(&channel->ovmasks, nick, (void *)ovmask);
ovmask |= NICKHALFOP;
else
ovmask &= ~NICKHALFOP;
hash_insert(&channel->ovmasks, nick, (void *)ovmask);
break;
case 'v':
nick = irc_line_elem(line, cur_arg + 3);
if (!hash_includes(&channel->ovmasks, nick))
return ERR_PROTOCOL;
ovmask = (long int)hash_remove(&channel->ovmasks, nick);
if (add)
ovmask |= NICKVOICED;
else
ovmask &= ~NICKVOICED;
hash_insert(&channel->ovmasks, nick, (void *)ovmask);
break;
} }
return OK_COPY; return OK_COPY;
} }
@ -2147,12 +2106,26 @@ struct link_server *irc_server_new(struct link *link, connection_t *conn)
irc_lag_init(s); irc_lag_init(s);
array_init(&s->chanmodes); array_init(&s->chanmodes);
s->prefixes = NULL;
s->usermodes = NULL;
server_init_modes(s);
return s;
}
static void server_init_modes(struct link_server *s)
{
// Default values used if CHANMODES is not specified by the server // Default values used if CHANMODES is not specified by the server
array_push(&s->chanmodes, bip_strdup("beHIq")); array_push(&s->chanmodes, bip_strdup("beHIq"));
array_push(&s->chanmodes, bip_strdup("k")); array_push(&s->chanmodes, bip_strdup("k"));
array_push(&s->chanmodes, bip_strdup("fjl")); array_push(&s->chanmodes, bip_strdup("fjl"));
array_push(&s->chanmodes, bip_strdup("fjl")); array_push(&s->chanmodes, bip_strdup("fjl"));
return s;
// Default values used if prefix is not specified by the server
s->prefixes = bip_realloc(s->prefixes, sizeof(*s->prefixes) * 3);
s->usermodes = bip_realloc(s->usermodes, sizeof(s->usermodes) * 3);
strcpy(s->prefixes, "@+");
strcpy(s->usermodes, "ov");
} }
void irc_server_free(struct link_server *s) void irc_server_free(struct link_server *s)
@ -2168,6 +2141,10 @@ void irc_server_free(struct link_server *s)
char *ptr; char *ptr;
array_each(&s->chanmodes, i, ptr) array_each(&s->chanmodes, i, ptr)
free(ptr); free(ptr);
MAYFREE(s->prefixes);
MAYFREE(s->usermodes);
hash_iterator_t hi; hash_iterator_t hi;
for (hash_it_init(&s->channels, &hi); hash_it_item(&hi); for (hash_it_init(&s->channels, &hi); hash_it_item(&hi);
hash_it_next(&hi)) { hash_it_next(&hi)) {
@ -2247,6 +2224,8 @@ void irc_server_shutdown(struct link_server *s)
free(cur); free(cur);
array_deinit(&s->chanmodes); array_deinit(&s->chanmodes);
server_init_modes(s);
if (!s->nick) if (!s->nick)
return; return;
if (LINK(s)->prev_nick) if (LINK(s)->prev_nick)
@ -2696,3 +2675,56 @@ static void server_set_chanmodes(struct link_server *l, const char *modes)
array_push(&l->chanmodes, dup); array_push(&l->chanmodes, dup);
} }
} }
static void server_set_prefix(struct link_server *s, const char *modes)
{
char * end_mode;
unsigned int len;
mylog(LOG_DEBUG, "[%s] Set user modes", LINK(s)->name);
// PREFIX=(mode)prefix
end_mode = strchr(modes + 1, ')'); // skip '('
if (*modes != '(' || !end_mode) {
mylog(LOG_WARN, "[%s] Unable to parse PREFIX parameter", LINK(s)->name);
return;
}
len = end_mode - modes - 1; // len of mode without '('
if (len * 2 + 2 != strlen(modes)) {
mylog(LOG_WARN, "[%s] Unable to parse PREFIX parameter", LINK(s)->name);
return;
}
s->prefixes = bip_realloc(s->prefixes, sizeof(*s->prefixes) * (len + 1));
s->usermodes = bip_realloc(s->usermodes, sizeof(s->usermodes) * (len + 1));
memcpy(s->prefixes, modes + 1, len);
s->prefixes[len] = 0;
memcpy(s->usermodes, end_mode + 1, len);
s->usermodes[len] = 0;
mylog(LOG_DEBUGVERB, "[%s] user prefix: '%s'", LINK(s)->name, s->prefixes);
mylog(LOG_DEBUGVERB, "[%s] user modes: '%s'", LINK(s)->name, s->usermodes);
}
// Return the position (*1 based*) of car in str, else -1
static int get_index(const char* str, char car)
{
char *cur;
if ((cur = strchr(str, car)))
return cur - str + 1;
else
return 0;
}
static int fls(int v)
{
unsigned int r = 0;
while (v >>= 1)
r++;
return r;
}

View File

@ -36,10 +36,6 @@ struct server {
#define server_new() bip_calloc(sizeof(struct server), 1) #define server_new() bip_calloc(sizeof(struct server), 1)
#define NICKOP 1
#define NICKHALFOP (1<<1)
#define NICKVOICED (1<<2)
struct channel { struct channel {
char *name; char *name;
char *mode; char *mode;
@ -234,6 +230,10 @@ struct link_server {
/* chanmodes */ /* chanmodes */
array_t chanmodes; array_t chanmodes;
/* user modes */
char *prefixes;
char *usermodes;
}; };
typedef struct bip { typedef struct bip {