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:
parent
e88d7da760
commit
73483ff088
246
src/irc.c
246
src/irc.c
@ -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;
|
||||||
|
}
|
||||||
|
@ -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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user