Mercurial > irccd
changeset 957:3167c51f0c84
irccd: rework events
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 21 Jan 2021 15:34:25 +0100 |
parents | 5e682f1cebcc |
children | 533639ec5e9c |
files | lib/irccd/event.c lib/irccd/event.h lib/irccd/irccd.c lib/irccd/js-plugin.c lib/irccd/server.c lib/irccd/server.h lib/irccd/util.h |
diffstat | 7 files changed, 596 insertions(+), 331 deletions(-) [+] |
line wrap: on
line diff
--- a/lib/irccd/event.c Thu Jan 21 09:49:42 2021 +0100 +++ b/lib/irccd/event.c Thu Jan 21 15:34:25 2021 +0100 @@ -24,86 +24,6 @@ #include "event.h" #include "server.h" -static inline void -scan(char **line, char **str) -{ - char *p = strchr(*line, ' '); - - if (p) - *p = '\0'; - - *str = *line; - *line = p ? p + 1 : strchr(*line, '\0'); -} - -bool -irc_event_is_ctcp(const char *line) -{ - size_t length; - - if (!line) - return false; - if ((length = strlen(line)) < 2) - return false; - - return line[0] == 0x1 && line[length - 1] == 0x1; -} - -char * -irc_event_ctcp(char *line) -{ - /* Skip first \001. */ - if (*line == '\001') - line++; - - /* Remove last \001. */ - line[strcspn(line, "\001")] = '\0'; - - if (strncmp(line, "ACTION ", 7) == 0) - line += 7; - - return line; -} - -bool -irc_event_parse(struct irc_event_msg *ev, const char *line) -{ - assert(ev); - assert(line); - - char *ptr = ev->buf; - size_t a; - - memset(ev, 0, sizeof (*ev)); - strlcpy(ev->buf, line, sizeof (ev->buf)); - - /* - * IRC message is defined as following: - * - * [:prefix] command arg1 arg2 [:last-argument] - */ - if (*line == ':') - scan((++ptr, &ptr), &ev->prefix); /* prefix */ - - scan(&ptr, &ev->cmd); /* command */ - - /* And finally arguments. */ - for (a = 0; *ptr && a < IRC_ARGS_MAX; ++a) { - if (*ptr == ':') { - ev->args[a] = ptr + 1; - ptr = strchr(ptr, '\0'); - } else - scan(&ptr, &ev->args[a]); - } - - if (a >= IRC_ARGS_MAX) - return errno = EMSGSIZE, false; - if (ev->cmd == NULL) - return errno = EBADMSG, false; - - return true; -} - bool irc_event_str(const struct irc_event *ev, char *str, size_t strsz) { @@ -114,82 +34,144 @@ switch (ev->type) { case IRC_EVENT_CONNECT: - written = snprintf(str, strsz, "EVENT-CONNECT %s", ev->server->name); + written = snprintf(str, strsz, "EVENT-CONNECT %s", + ev->server->name); break; case IRC_EVENT_DISCONNECT: - written = snprintf(str, strsz, "EVENT-DISCONNECT %s", ev->server->name); + written = snprintf(str, strsz, "EVENT-DISCONNECT %s", + ev->server->name); break; case IRC_EVENT_INVITE: - written = snprintf(str, strsz, "EVENT-INVITE %s %s %s %s", ev->server->name, - ev->msg.prefix, ev->msg.args[0], ev->msg.args[1]); + written = snprintf(str, strsz, "EVENT-INVITE %s %s %s %s", + ev->server->name, ev->invite.origin, ev->invite.channel, + ev->invite.target); break; case IRC_EVENT_JOIN: - written = snprintf(str, strsz, "EVENT-JOIN %s %s %s", ev->server->name, - ev->msg.prefix, ev->msg.args[0]); + written = snprintf(str, strsz, "EVENT-JOIN %s %s %s", + ev->server->name, ev->join.origin, ev->join.channel); break; case IRC_EVENT_KICK: - written = snprintf(str, strsz, "EVENT-KICK %s %s %s %s %s", ev->server->name, - ev->msg.prefix, ev->msg.args[0], ev->msg.args[1], - ev->msg.args[2] ? ev->msg.args[2] : ""); + written = snprintf(str, strsz, "EVENT-KICK %s %s %s %s %s", + ev->server->name, ev->kick.origin, ev->kick.channel, + ev->kick.target, ev->kick.reason ? ev->kick.reason : ""); break; case IRC_EVENT_ME: - written = snprintf(str, strsz, "EVENT-ME %s %s %s %s", ev->server->name, - ev->msg.prefix, ev->msg.args[0], ev->msg.args[1]); + written = snprintf(str, strsz, "EVENT-ME %s %s %s %s", + ev->server->name, ev->message.origin, ev->message.channel, + ev->message.message); break; case IRC_EVENT_MESSAGE: - written = snprintf(str, strsz, "EVENT-MESSAGE %s %s %s %s", ev->server->name, - ev->msg.prefix, ev->msg.args[0], ev->msg.args[1]); + written = snprintf(str, strsz, "EVENT-MESSAGE %s %s %s %s", + ev->server->name, ev->message.origin, ev->message.channel, + ev->message.message); break; case IRC_EVENT_MODE: written = snprintf(str, strsz, "EVENT-MODE %s %s %s %s %s %s %s", - ev->server->name, ev->msg.prefix, ev->msg.args[0], - ev->msg.args[1] ? ev->msg.args[1] : "", - ev->msg.args[2] ? ev->msg.args[2] : "", - ev->msg.args[3] ? ev->msg.args[3] : "", - ev->msg.args[4] ? ev->msg.args[4] : ""); + ev->server->name, ev->mode.origin, ev->mode.channel, + ev->mode.mode, + ev->mode.limit ? ev->mode.limit : "", + ev->mode.user ? ev->mode.user : "", + ev->mode.mask ? ev->mode.mask : ""); break; case IRC_EVENT_NICK: - written = snprintf(str, strsz, "EVENT-NICK %s %s %s", ev->server->name, - ev->msg.prefix, ev->msg.args[0]); + written = snprintf(str, strsz, "EVENT-NICK %s %s %s", + ev->server->name, ev->nick.origin, ev->nick.nickname); break; case IRC_EVENT_NOTICE: - written = snprintf(str, strsz, "EVENT-NOTICE %s %s %s %s", ev->server->name, - ev->msg.prefix, ev->msg.args[0], ev->msg.args[1]); + written = snprintf(str, strsz, "EVENT-NOTICE %s %s %s %s", + ev->server->name, ev->notice.origin, ev->notice.channel, + ev->notice.notice); break; case IRC_EVENT_PART: - written = snprintf(str, strsz, "EVENT-PART %s %s %s %s", ev->server->name, - ev->msg.prefix, ev->msg.args[0], - ev->msg.args[1] ? ev->msg.args[1] : ""); + written = snprintf(str, strsz, "EVENT-PART %s %s %s %s", + ev->server->name, ev->part.origin, ev->part.channel, + ev->part.reason ? ev->part.reason : ""); break; case IRC_EVENT_TOPIC: - written = snprintf(str, strsz, "EVENT-TOPIC %s %s %s %s", ev->server->name, - ev->msg.prefix, ev->msg.args[0], ev->msg.args[1]); + written = snprintf(str, strsz, "EVENT-TOPIC %s %s %s %s", + ev->server->name, ev->topic.origin, ev->topic.channel, + ev->topic.topic); break; case IRC_EVENT_WHOIS: - snprintf(str, strsz, "EVENT-WHOIS %s %s %s %s %s ", - ev->server->name, ev->whois->nickname, ev->whois->username, - ev->whois->realname, ev->whois->hostname); - - for (size_t i = 0; i < ev->whois->channelsz; ++i) { - size_t s; - - /* Concat channel and their modes. */ - strlcat(str, (char []) {ev->whois->channels[i].mode, '\0' }, strsz); - strlcat(str, ev->whois->channels[i].channel, strsz); - - if ((s = strlcat(str, " ", strsz)) >= strsz) - goto emsgsize; - - written = s; - } + snprintf(str, strsz, "EVENT-WHOIS %s %s %s %s %s %s", + ev->server->name, ev->whois.nickname, ev->whois.username, + ev->whois.realname, ev->whois.hostname, ev->whois.channels); break; default: break; } return written > 0; +} -emsgsize: - errno = EMSGSIZE; - return false; +void +irc_event_finish(struct irc_event *ev) +{ + assert(ev); + + switch (ev->type) { + case IRC_EVENT_INVITE: + free(ev->invite.origin); + free(ev->invite.channel); + break; + case IRC_EVENT_JOIN: + free(ev->join.origin); + free(ev->join.channel); + break; + case IRC_EVENT_KICK: + free(ev->kick.origin); + free(ev->kick.channel); + free(ev->kick.target); + free(ev->kick.reason); + break; + case IRC_EVENT_COMMAND: + case IRC_EVENT_ME: + case IRC_EVENT_MESSAGE: + free(ev->message.origin); + free(ev->message.channel); + free(ev->message.message); + break; + case IRC_EVENT_MODE: + free(ev->mode.origin); + free(ev->mode.channel); + free(ev->mode.mode); + free(ev->mode.limit); + free(ev->mode.user); + free(ev->mode.mask); + break; + case IRC_EVENT_NAMES: + free(ev->names.channel); + free(ev->names.names); + break; + case IRC_EVENT_NICK: + free(ev->nick.origin); + free(ev->nick.nickname); + break; + case IRC_EVENT_NOTICE: + free(ev->notice.origin); + free(ev->notice.channel); + free(ev->notice.notice); + break; + case IRC_EVENT_PART: + free(ev->part.origin); + free(ev->part.channel); + free(ev->part.reason); + break; + case IRC_EVENT_TOPIC: + free(ev->topic.origin); + free(ev->topic.channel); + free(ev->topic.topic); + break; + case IRC_EVENT_WHOIS: + free(ev->whois.nickname); + free(ev->whois.username); + free(ev->whois.realname); + free(ev->whois.hostname); + free(ev->whois.channels); + break; + default: + break; + } + + memset(ev, 0, sizeof (*ev)); }
--- a/lib/irccd/event.h Thu Jan 21 09:49:42 2021 +0100 +++ b/lib/irccd/event.h Thu Jan 21 15:34:25 2021 +0100 @@ -26,51 +26,126 @@ struct irc_server; +/* + * This enumeration indicates which kind of event (not mandatory IRC related) + * happened on a server. It is used in conjunction with underlying sub + * structures of type irc_event_* that is store into the generic irc_event + * structure. + * + * Some events may contain specific data that is accessible via the anonymous + * union inside the struct irc_event, refer to the enumerator description to + * check which member must be accessed in that case. + */ enum irc_event_type { - IRC_EVENT_UNKNOWN, - IRC_EVENT_COMMAND, - IRC_EVENT_CONNECT, - IRC_EVENT_DISCONNECT, - IRC_EVENT_INVITE, - IRC_EVENT_JOIN, - IRC_EVENT_KICK, - IRC_EVENT_ME, - IRC_EVENT_MESSAGE, - IRC_EVENT_MODE, - IRC_EVENT_NAMES, - IRC_EVENT_NICK, - IRC_EVENT_NOTICE, - IRC_EVENT_PART, - IRC_EVENT_TOPIC, - IRC_EVENT_WHOIS + IRC_EVENT_UNKNOWN, /* Unknown or private use. */ + IRC_EVENT_COMMAND, /* Use irc_event_message. */ + IRC_EVENT_CONNECT, /* No specific data. */ + IRC_EVENT_DISCONNECT, /* No specific data. */ + IRC_EVENT_INVITE, /* Use irc_event_invite. */ + IRC_EVENT_JOIN, /* Use irc_event_join. */ + IRC_EVENT_KICK, /* Use irc_event_kick. */ + IRC_EVENT_ME, /* Use irc_event_message */ + IRC_EVENT_MESSAGE, /* Use irc_event_message. */ + IRC_EVENT_MODE, /* Use irc_event_mode. */ + IRC_EVENT_NAMES, /* Use irc_event_names. */ + IRC_EVENT_NICK, /* Use irc_event_nick. */ + IRC_EVENT_NOTICE, /* Use irc_event_notice. */ + IRC_EVENT_PART, /* Use irc_event_part. */ + IRC_EVENT_TOPIC, /* Use irc_event_topic. */ + IRC_EVENT_WHOIS /* Use irc_event_whois. */ +}; + +struct irc_event_invite { + char *origin; + char *channel; + char *target; +}; + +struct irc_event_join { + char *origin; + char *channel; +}; + +struct irc_event_kick { + char *origin; + char *channel; + char *target; + char *reason; }; -struct irc_event_msg { - char *prefix; - char *cmd; - char *args[IRC_ARGS_MAX]; - char buf[IRC_MESSAGE_MAX]; +struct irc_event_message { + char *origin; + char *channel; + char *message; +}; + +struct irc_event_mode { + char *origin; + char *channel; + char *mode; + char *limit; + char *user; + char *mask; +}; + +struct irc_event_names { + char *channel; + char *names; +}; + +struct irc_event_nick { + char *origin; + char *nickname; +}; + +struct irc_event_notice { + char *origin; + char *channel; + char *notice; +}; + +struct irc_event_part { + char *origin; + char *channel; + char *reason; +}; + +struct irc_event_topic { + char *origin; + char *channel; + char *topic; +}; + +struct irc_event_whois { + char *nickname; + char *username; + char *realname; + char *hostname; + char *channels; }; struct irc_event { enum irc_event_type type; struct irc_server *server; union { - struct irc_event_msg msg; - struct irc_server_whois *whois; + struct irc_event_invite invite; + struct irc_event_join join; + struct irc_event_kick kick; + struct irc_event_message message; + struct irc_event_mode mode; + struct irc_event_names names; + struct irc_event_nick nick; + struct irc_event_notice notice; + struct irc_event_part part; + struct irc_event_topic topic; + struct irc_event_whois whois; }; }; bool -irc_event_is_ctcp(const char *); - -char * -irc_event_ctcp(char *); - -bool -irc_event_parse(struct irc_event_msg *, const char *); - -bool irc_event_str(const struct irc_event *, char *, size_t); +void +irc_event_finish(struct irc_event *); + #endif /* !IRCCD_EVENT_H */
--- a/lib/irccd/irccd.c Thu Jan 21 09:49:42 2021 +0100 +++ b/lib/irccd/irccd.c Thu Jan 21 15:34:25 2021 +0100 @@ -76,22 +76,26 @@ cc = ev->server->commandchar; ccsz = strlen(cc); - return strncmp(ev->msg.args[1], cc, ccsz) == 0 && - strncmp(ev->msg.args[1] + ccsz, p->name, strlen(p->name)) == 0; + return strncmp(ev->message.message, cc, ccsz) == 0 && + strncmp(ev->message.message + ccsz, p->name, strlen(p->name)) == 0; } static struct irc_event * to_command(const struct irc_plugin *p, struct irc_event *ev) { - char *s; + char *action; + + /* Convert "!test foo bar" to "foo bar" */ + action = ev->message.message + strlen(ev->server->commandchar) + strlen(p->name); + + while (*action && isspace(*action)) + ++action; + + action = strdup(action); + free(ev->message.message); ev->type = IRC_EVENT_COMMAND; - ev->msg.args[1] = ev->msg.args[1] + strlen(ev->server->commandchar) + strlen(p->name); - - for (s = ev->msg.args[1]; *s && isspace(*s); ) - ++s; - - ev->msg.args[1] = s; + ev->message.message = action; return ev; } @@ -102,7 +106,7 @@ switch (ev->type) { case IRC_EVENT_COMMAND: return irc_rule_matchlist(irc.rules, irc.rulesz, ev->server->name, - ev->msg.args[0], ev->msg.prefix, p->name, "onCommand"); + ev->message.channel, ev->message.origin, p->name, "onCommand"); case IRC_EVENT_CONNECT: return irc_rule_matchlist(irc.rules, irc.rulesz, ev->server->name, NULL, NULL, p->name, "onConnect"); @@ -111,40 +115,41 @@ NULL, NULL, p->name, "onDisconnect"); case IRC_EVENT_INVITE: return irc_rule_matchlist(irc.rules, irc.rulesz, ev->server->name, - ev->msg.args[1], ev->msg.prefix, p->name, "onInvite"); + ev->invite.channel, ev->invite.origin, p->name, "onInvite"); case IRC_EVENT_JOIN: return irc_rule_matchlist(irc.rules, irc.rulesz, ev->server->name, - ev->msg.args[0], ev->msg.prefix, p->name, "onJoin"); + ev->join.channel, ev->join.origin, p->name, "onJoin"); case IRC_EVENT_KICK: return irc_rule_matchlist(irc.rules, irc.rulesz, ev->server->name, - ev->msg.args[0], ev->msg.prefix, p->name, "onKick"); + ev->kick.channel, ev->kick.origin, p->name, "onKick"); break; case IRC_EVENT_ME: return irc_rule_matchlist(irc.rules, irc.rulesz, ev->server->name, - ev->msg.args[0], ev->msg.prefix, p->name, "onMe"); + ev->message.channel, ev->message.origin, p->name, "onMe"); case IRC_EVENT_MESSAGE: return irc_rule_matchlist(irc.rules, irc.rulesz, ev->server->name, - ev->msg.args[0], ev->msg.prefix, p->name, "onMessage"); + ev->message.channel, ev->message.origin, p->name, "onMessage"); case IRC_EVENT_MODE: return irc_rule_matchlist(irc.rules, irc.rulesz, ev->server->name, - ev->msg.args[0], ev->msg.prefix, p->name, "onMode"); + ev->mode.channel, ev->mode.origin, p->name, "onMode"); case IRC_EVENT_NAMES: return irc_rule_matchlist(irc.rules, irc.rulesz, ev->server->name, - ev->msg.args[0], NULL, p->name, "onNames"); + ev->names.channel, NULL, p->name, "onNames"); case IRC_EVENT_NICK: return irc_rule_matchlist(irc.rules, irc.rulesz, ev->server->name, - NULL, ev->msg.prefix, p->name, "onNick"); + NULL, ev->nick.origin, p->name, "onNick"); case IRC_EVENT_NOTICE: return irc_rule_matchlist(irc.rules, irc.rulesz, ev->server->name, - ev->msg.args[0], ev->msg.prefix, p->name, "onNotice"); + ev->notice.channel, ev->notice.origin, p->name, "onNotice"); case IRC_EVENT_PART: return irc_rule_matchlist(irc.rules, irc.rulesz, ev->server->name, - ev->msg.args[0], ev->msg.prefix, p->name, "onPart"); + ev->part.channel, ev->part.origin, p->name, "onPart"); case IRC_EVENT_TOPIC: return irc_rule_matchlist(irc.rules, irc.rulesz, ev->server->name, - ev->msg.args[0], ev->msg.prefix, p->name, "onTopic"); + ev->topic.channel, ev->topic.origin, p->name, "onTopic"); case IRC_EVENT_WHOIS: - return true; + return irc_rule_matchlist(irc.rules, irc.rulesz, ev->server->name, + NULL, NULL, p->name, "onWhois"); default: return true; } @@ -278,6 +283,7 @@ while (irc_server_poll(s, &ev)) { broadcast(&ev); invoke(&ev); + irc_event_finish(&ev); } } }
--- a/lib/irccd/js-plugin.c Thu Jan 21 09:49:42 2021 +0100 +++ b/lib/irccd/js-plugin.c Thu Jan 21 15:34:25 2021 +0100 @@ -87,12 +87,12 @@ static void push_names(duk_context *ctx, const struct irc_event *ev) { - const struct irc_channel *ch = irc_server_find(ev->server, ev->msg.args[0]); + char *token, *p = ev->names.names; duk_push_array(ctx); - for (size_t i = 0; i < ch->usersz; ++i) { - duk_push_string(ctx, ch->users[i].nickname); + for (size_t i = 0; (token = strtok_r(p, " ", &p)); ++i) { + duk_push_string(ctx, irc_server_strip(ev->server, token).name); duk_put_prop_index(ctx, -2, i); } } @@ -100,22 +100,25 @@ static void push_whois(duk_context *ctx, const struct irc_event *ev) { + char *token, *p = ev->whois.channels; + struct irc_server_namemode nm; + duk_push_object(ctx); - duk_push_string(ctx, ev->whois->nickname); + duk_push_string(ctx, ev->whois.nickname); duk_put_prop_string(ctx, -2, "nickname"); - duk_push_string(ctx, ev->whois->username); + duk_push_string(ctx, ev->whois.username); duk_put_prop_string(ctx, -2, "username"); - duk_push_string(ctx, ev->whois->realname); + duk_push_string(ctx, ev->whois.realname); duk_put_prop_string(ctx, -2, "realname"); - duk_push_string(ctx, ev->whois->hostname); + duk_push_string(ctx, ev->whois.hostname); duk_put_prop_string(ctx, -2, "hostname"); duk_push_array(ctx); - for (size_t i = 0; i < ev->whois->channelsz; ++i) { - printf("[%s] = [%c]\n", ev->whois->channels[i].channel, ev->whois->channels[i].mode); + for (size_t i = 0; (token = strtok_r(p, " ", &p)); ++i) { + nm = irc_server_strip(ev->server, token); duk_push_object(ctx); - duk_push_string(ctx, ev->whois->channels[i].channel); + duk_push_string(ctx, nm.name); duk_put_prop_string(ctx, -2, "channel"); - duk_push_sprintf(ctx, "%c", ev->whois->channels[i].mode); + duk_push_sprintf(ctx, "%c", nm.mode); duk_put_prop_string(ctx, -2, "mode"); duk_put_prop_index(ctx, -2, i); } @@ -304,8 +307,8 @@ switch (ev->type) { case IRC_EVENT_COMMAND: - call(plg, "onCommand", "Ss ss", ev->server, ev->msg.prefix, - ev->msg.args[0], ev->msg.args[1]); + call(plg, "onCommand", "Ss ss", ev->server, ev->message.origin, + ev->message.channel, ev->message.message); break; case IRC_EVENT_CONNECT: call(plg, "onConnect", "S", ev->server); @@ -314,49 +317,49 @@ call(plg, "onDisconnect", "S", ev->server); break; case IRC_EVENT_INVITE: - call(plg, "onInvite", "Ss s", ev->server, ev->msg.prefix, - ev->msg.args[1]); + call(plg, "onInvite", "Ss s", ev->server, ev->invite.origin, + ev->invite.channel); break; case IRC_EVENT_JOIN: - call(plg, "onJoin", "Ss s", ev->server, ev->msg.prefix, - ev->msg.args[0]); + call(plg, "onJoin", "Ss s", ev->server, ev->join.origin, + ev->join.channel); break; case IRC_EVENT_KICK: - call(plg, "onKick", "Ss sss", ev->server, ev->msg.prefix, - ev->msg.args[0], ev->msg.args[1], ev->msg.args[2]); + call(plg, "onKick", "Ss sss", ev->server, ev->kick.origin, + ev->kick.channel, ev->kick.target, ev->kick.reason); break; case IRC_EVENT_ME: - call(plg, "onMe", "Ss ss", ev->server, ev->msg.prefix, - ev->msg.args[0], ev->msg.args[1]); + call(plg, "onMe", "Ss ss", ev->server, ev->message.origin, + ev->message.channel, ev->message.message); break; case IRC_EVENT_MESSAGE: - call(plg, "onMessage", "Ss ss", ev->server, ev->msg.prefix, - ev->msg.args[0], ev->msg.args[1]); + call(plg, "onMessage", "Ss ss", ev->server, ev->message.origin, + ev->message.channel, ev->message.message); break; case IRC_EVENT_MODE: - call(plg, "onMode", "Ss sss ss", ev->server, ev->msg.prefix, - ev->msg.args[0], ev->msg.args[1], ev->msg.args[2], - ev->msg.args[3], ev->msg.args[4]); + call(plg, "onMode", "Ss sss ss", ev->server, ev->mode.origin, + ev->mode.channel, ev->mode.mode, ev->mode.limit, + ev->mode.user, ev->mode.mask); break; case IRC_EVENT_NAMES: - call(plg, "onNames", "Ss x", ev->server, ev->msg.args[1], + call(plg, "onNames", "Ss x", ev->server, ev->names.channel, push_names, ev); break; case IRC_EVENT_NICK: - call(plg, "onNick", "Ss s", ev->server, ev->msg.prefix, - ev->msg.args[0]); + call(plg, "onNick", "Ss s", ev->server, ev->nick.origin, + ev->nick.nickname); break; case IRC_EVENT_NOTICE: - call(plg, "onNotice", "Ss ss", ev->server, ev->msg.prefix, - ev->msg.args[0], ev->msg.args[1]); + call(plg, "onNotice", "Ss ss", ev->server, ev->notice.origin, + ev->notice.channel, ev->notice.notice); break; case IRC_EVENT_PART: - call(plg, "onPart", "Ss ss", ev->server, ev->msg.prefix, - ev->msg.args[0], ev->msg.args[1]); + call(plg, "onPart", "Ss ss", ev->server, ev->part.origin, + ev->part.channel, ev->part.reason); break; case IRC_EVENT_TOPIC: - call(plg, "onTopic", "Ss ss", ev->server, ev->msg.prefix, - ev->msg.args[0], ev->msg.args[1]); + call(plg, "onTopic", "Ss ss", ev->server, ev->topic.origin, + ev->topic.channel, ev->topic.topic); break; case IRC_EVENT_WHOIS: call(plg, "onWhois", "Sx", ev->server, push_whois, ev);
--- a/lib/irccd/server.c Thu Jan 21 09:49:42 2021 +0100 +++ b/lib/irccd/server.c Thu Jan 21 15:34:25 2021 +0100 @@ -49,12 +49,85 @@ char host[IRC_HOST_MAX]; }; +struct message { + char *prefix; + char *cmd; + char *args[IRC_ARGS_MAX]; + char buf[IRC_MESSAGE_MAX]; +}; + +static inline void +scan(char **line, char **str) +{ + char *p = strchr(*line, ' '); + + if (p) + *p = '\0'; + + *str = *line; + *line = p ? p + 1 : strchr(*line, '\0'); +} + +static bool +parse(struct message *msg, const char *line) +{ + char *ptr = msg->buf; + size_t a; + + memset(msg, 0, sizeof (*msg)); + strlcpy(msg->buf, line, sizeof (msg->buf)); + + /* + * IRC message is defined as following: + * + * [:prefix] command arg1 arg2 [:last-argument] + */ + if (*ptr == ':') + scan((++ptr, &ptr), &msg->prefix); /* prefix */ + + scan(&ptr, &msg->cmd); /* command */ + + /* And finally arguments. */ + for (a = 0; *ptr && a < IRC_ARGS_MAX; ++a) { + if (*ptr == ':') { + msg->args[a] = ptr + 1; + ptr = strchr(ptr, '\0'); + } else + scan(&ptr, &msg->args[a]); + } + + if (a >= IRC_ARGS_MAX) + return errno = EMSGSIZE, false; + if (msg->cmd == NULL) + return errno = EBADMSG, false; + + return true; +} + +static inline char +sym(const struct irc_server *s, char mode) +{ + for (size_t i = 0; i < sizeof (s->prefixes); ++i) + if (s->prefixes[i].mode == mode) + return s->prefixes[i].token; + + return '?'; +} + +static inline bool +is_self(const struct irc_server *s, const char *nick) +{ + return strncmp(s->nickname, nick, strlen(s->nickname)) == 0; +} + static int cmp_channel(const struct irc_channel *c1, const struct irc_channel *c2) { return strcmp(c1->name, c2->name); } +#if 0 + static const struct origin * parse_origin(const char *prefix) { @@ -71,6 +144,8 @@ return &origin; } +#endif + static void add_nick(const struct irc_server *s, struct irc_channel *ch, const char *nick) { @@ -116,6 +191,35 @@ IRC_SET_ALLOC_REMOVE(&s->channels, &s->channelsz, ch); } +bool +is_ctcp(const char *line) +{ + size_t length; + + if (!line) + return false; + if ((length = strlen(line)) < 2) + return false; + + return line[0] == 0x1 && line[length - 1] == 0x1; +} + +char * +ctcp(char *line) +{ + /* Skip first \001. */ + if (*line == '\001') + line++; + + /* Remove last \001. */ + line[strcspn(line, "\001")] = '\0'; + + if (strncmp(line, "ACTION ", 7) == 0) + line += 7; + + return line; +} + static void read_support_prefix(struct irc_server *s, const char *value) { @@ -140,8 +244,10 @@ } static void -handle_connect(struct irc_server *s, struct irc_event *ev) +handle_connect(struct irc_server *s, struct irc_event *ev, struct message *msg) { + (void)msg; + s->state = IRC_SERVER_STATE_CONNECTED; /* Now join all channels that were requested. */ @@ -152,15 +258,15 @@ } static void -handle_support(struct irc_server *s, struct irc_event *ev) +handle_support(struct irc_server *s, struct irc_event *ev, struct message *msg) { (void)ev; char key[64]; char value[64]; - for (size_t i = 2; ev->msg.args[i]; ++i) { - if (sscanf(ev->msg.args[i], "%63[^=]=%63s", key, value) != 2) + for (size_t i = 0; i < IRC_UTIL_SIZE(msg->args) && msg->args[i]; ++i) { + if (sscanf(msg->args[i], "%63[^=]=%63s", key, value) != 2) continue; if (strcmp(key, "PREFIX") == 0) @@ -171,191 +277,259 @@ } static void -handle_invite(struct irc_server *s, struct irc_event *ev) +handle_invite(struct irc_server *s, struct irc_event *ev, struct message *msg) { - if (strcmp(ev->msg.args[0], s->nickname) == 0 && s->flags & IRC_SERVER_FLAGS_JOIN_INVITE) - irc_server_join(s, ev->msg.args[1], NULL); + ev->type = IRC_EVENT_INVITE; + ev->invite.origin = strdup(msg->args[0]); + ev->invite.channel = strdup(msg->args[1]); + ev->invite.target = strdup(msg->args[2]); - ev->type = IRC_EVENT_INVITE; + if (is_self(s, ev->invite.target) && s->flags & IRC_SERVER_FLAGS_JOIN_INVITE) + irc_server_join(s, ev->invite.channel, NULL); } static void -handle_join(struct irc_server *s, struct irc_event *ev) +handle_join(struct irc_server *s, struct irc_event *ev, struct message *msg) { - add_channel(s, ev->msg.args[0], NULL, true); + ev->type = IRC_EVENT_JOIN; + ev->join.origin = strdup(msg->prefix); + ev->join.channel = strdup(msg->args[0]); - ev->type = IRC_EVENT_JOIN; + add_channel(s, ev->join.channel, NULL, true); } static void -handle_kick(struct irc_server *s, struct irc_event *ev) +handle_kick(struct irc_server *s, struct irc_event *ev, struct message *msg) { - struct irc_channel *ch = add_channel(s, ev->msg.args[0], NULL, true); + ev->type = IRC_EVENT_KICK; + ev->kick.origin = strdup(msg->prefix); + ev->kick.channel = strdup(msg->args[0]); + ev->kick.target = strdup(msg->args[1]); + ev->kick.reason = msg->args[2] ? strdup(msg->args[2]) : NULL; + + struct irc_channel *ch = add_channel(s, ev->kick.channel, NULL, true); /* * If the bot was kicked itself mark the channel as not joined and * rejoin it automatically if the option is set. */ - if (strcmp(ev->msg.args[1], s->nickname) == 0) { + if (is_self(s, ev->kick.target) == 0) { ch->joined = false; irc_channel_clear(ch); if (s->flags & IRC_SERVER_FLAGS_AUTO_REJOIN) irc_server_join(s, ch->name, ch->password); } else - irc_channel_remove(ch, ev->msg.args[1]); - - ev->type = IRC_EVENT_KICK; + irc_channel_remove(ch, ev->kick.target); } static void -handle_mode(struct irc_server *s, struct irc_event *ev) +handle_mode(struct irc_server *s, struct irc_event *ev, struct message *msg) { (void)s; (void)ev; + (void)msg; ev->type = IRC_EVENT_MODE; + ev->mode.origin = strdup(msg->prefix); + ev->mode.channel = strdup(msg->args[0]); + ev->mode.mode = strdup(msg->args[1]); + ev->mode.limit = msg->args[2] ? strdup(msg->args[2]) : NULL; + ev->mode.user = msg->args[3] ? strdup(msg->args[3]) : NULL; + ev->mode.mask = msg->args[4] ? strdup(msg->args[4]) : NULL; + + /* TODO: update nickname modes. */ } static void -handle_part(struct irc_server *s, struct irc_event *ev) +handle_part(struct irc_server *s, struct irc_event *ev, struct message *msg) { - const struct origin *origin = parse_origin(ev->msg.prefix); - struct irc_channel *ch = add_channel(s, ev->msg.args[0], NULL, true); - - if (strcmp(origin->nickname, s->nickname) == 0) - remove_channel(s, ch); + struct irc_channel *ch; ev->type = IRC_EVENT_PART; + ev->part.origin = strdup(msg->prefix); + ev->part.channel = strdup(msg->args[0]); + ev->part.reason = msg->args[1] ? strdup(msg->args[1]) : NULL; + + ch = add_channel(s, ev->part.channel, NULL, true); + + if (is_self(s, ev->part.origin) == 0) + remove_channel(s, ch); + else + irc_channel_remove(ch, ev->part.origin); } static void -handle_msg(struct irc_server *s, struct irc_event *ev) +handle_msg(struct irc_server *s, struct irc_event *ev, struct message *msg) { (void)s; + ev->message.origin = strdup(msg->prefix); + ev->message.channel = strdup(msg->args[0]); + /* * Detect CTCP commands which are PRIVMSG with a special boundaries. * * Example: * PRIVMSG jean :\001ACTION I'm eating\001. */ - if (irc_event_is_ctcp(ev->msg.args[1])) { + if (is_ctcp(msg->args[1])) { ev->type = IRC_EVENT_ME; - ev->msg.args[1] = irc_event_ctcp(ev->msg.args[1]); - } else + ev->message.message = strdup(ctcp(msg->args[1])); + } else { ev->type = IRC_EVENT_MESSAGE; + ev->message.message = strdup(msg->args[1]); + } } static void -handle_nick(struct irc_server *s, struct irc_event *ev) +handle_nick(struct irc_server *s, struct irc_event *ev, struct message *msg) { - const struct origin *origin = parse_origin(ev->msg.prefix); + ev->type = IRC_EVENT_NICK; + ev->nick.origin = strdup(msg->prefix); + ev->nick.nickname = strdup(msg->args[0]); /* Update nickname if it is myself. */ - if (strcmp(origin->nickname, s->nickname) == 0) - strlcpy(s->nickname, ev->msg.args[0], sizeof (s->nickname)); - - ev->type = IRC_EVENT_NICK; + if (is_self(s, ev->nick.origin) == 0) + strlcpy(s->nickname, ev->nick.nickname, sizeof (s->nickname)); } static void -handle_notice(struct irc_server *s, struct irc_event *ev) +handle_notice(struct irc_server *s, struct irc_event *ev, struct message *msg) { (void)s; ev->type = IRC_EVENT_NOTICE; + ev->notice.origin = strdup(msg->prefix); + ev->notice.channel = strdup(msg->args[0]); + ev->notice.notice = strdup(msg->args[1]); } static void -handle_topic(struct irc_server *s, struct irc_event *ev) +handle_topic(struct irc_server *s, struct irc_event *ev, struct message *msg) { (void)s; ev->type = IRC_EVENT_TOPIC; -} - -static void -handle_ping(struct irc_server *s, struct irc_event *ev) -{ - irc_server_send(s, "PONG %s", ev->msg.args[0]); + ev->topic.origin = strdup(msg->prefix); + ev->topic.channel = strdup(msg->args[0]); + ev->topic.topic = strdup(msg->args[1]); } static void -handle_names(struct irc_server *s, struct irc_event *ev) +handle_ping(struct irc_server *s, struct irc_event *ev, struct message *msg) { + (void)s; + (void)ev; + (void)msg; + + //irc_server_send(s, "PONG %s", args[1]); +} + +static void +handle_names(struct irc_server *s, struct irc_event *ev, struct message *msg) +{ + (void)s; + (void)ev; + (void)msg; + struct irc_channel *ch; char *p, *token; - ch = add_channel(s, ev->msg.args[2], NULL, true); + ch = add_channel(s, msg->args[2], NULL, true); - /* TODO: libcompat for strtok_r. */ - for (p = ev->msg.args[3]; (token = strtok_r(p, " ", &p)); ) + /* Track existing nicknames into the given channel. */ + for (p = msg->args[3]; (token = strtok_r(p, " ", &p)); ) if (strlen(token) > 0) add_nick(s, ch, token); } static void -handle_endofnames(struct irc_server *s, struct irc_event *ev) +handle_endofnames(struct irc_server *s, struct irc_event *ev, struct message *msg) { (void)s; + (void)ev; + (void)msg; + + FILE *fp; + const struct irc_channel *ch; + size_t length; ev->type = IRC_EVENT_NAMES; -} + ev->names.channel = strdup(msg->args[1]); + + /* Construct a string list for every user in the channel. */ + ch = irc_server_find(s, ev->names.channel); + fp = open_memstream(&ev->names.names, &length); + + for (size_t i = 0; i < ch->usersz; ++i) { + const struct irc_channel_user *u = &ch->users[i]; -static void -handle_whoisuser(struct irc_server *s, struct irc_event *ev) -{ - (void)ev; + if (u->mode) + fprintf(fp, "%c", sym(s, u->mode)); + + fprintf(fp, "%s", u->nickname); - strlcpy(s->whois.nickname, ev->msg.args[1], sizeof (s->whois.nickname)); - strlcpy(s->whois.username, ev->msg.args[2], sizeof (s->whois.username)); - strlcpy(s->whois.hostname, ev->msg.args[3], sizeof (s->whois.hostname)); - strlcpy(s->whois.realname, ev->msg.args[5], sizeof (s->whois.realname)); + if (i + 1 < ch->usersz) + fputc(' ', fp); + } + + fclose(fp); } static void -add_whois_channel(struct irc_server *s, const char *channel) +handle_whoisuser(struct irc_server *s, struct irc_event *ev, struct message *msg) { - char mode = 0; - - s->whois.channels = irc_util_reallocarray(s->whois.channels, - ++s->whois.channelsz, sizeof (*s->whois.channels)); + (void)s; + (void)msg; - /* TODO: split this to refactor add_nick. */ - for (size_t i = 0; i < IRC_UTIL_SIZE(s->prefixes); ++i) { - if (channel[0] == s->prefixes[i].token) { - mode = s->prefixes[i].mode; - ++channel; - break; - } - } - - s->whois.channels[s->whois.channelsz - 1].mode = mode; - strlcpy(s->whois.channels[s->whois.channelsz - 1].channel, channel, - sizeof (s->whois.channels[0].channel)); + s->bufwhois.nickname = strdup(msg->args[1]); + s->bufwhois.username = strdup(msg->args[2]); + s->bufwhois.hostname = strdup(msg->args[3]); + s->bufwhois.realname = strdup(msg->args[5]); } static void -handle_whoischannels(struct irc_server *s, struct irc_event *ev) +handle_whoischannels(struct irc_server *s, struct irc_event *ev, struct message *msg) { - char *p, *token; + (void)ev; + + size_t curlen, reqlen; + + curlen = s->bufwhois.channels ? strlen(s->bufwhois.channels) : 0; + reqlen = strlen(msg->args[2]); - for (p = ev->msg.args[2]; (token = strtok_r(p, " ", &p)); ) - if (strlen(token) > 0) - add_whois_channel(s, token); + /* + * If there is already something, add a space at the end of the current + * buffer. + */ + if (curlen > 0) + reqlen++; + + /* Now, don't forget */ + s->bufwhois.channels = irc_util_realloc(s->bufwhois.channels, reqlen + 1); + + if (curlen > 0) { + strcat(s->bufwhois.channels, " "); + strcat(s->bufwhois.channels, msg->args[2]); + } else + strcpy(s->bufwhois.channels, msg->args[2]); } static void -handle_endofwhois(struct irc_server *s, struct irc_event *ev) +handle_endofwhois(struct irc_server *s, struct irc_event *ev, struct message *msg) { + (void)msg; + ev->type = IRC_EVENT_WHOIS; - ev->whois = &s->whois; + ev->whois = s->bufwhois; + + memset(&s->bufwhois, 0, sizeof (s->bufwhois)); } static const struct handler { const char *command; - void (*handle)(struct irc_server *, struct irc_event *); + void (*handle)(struct irc_server *, struct irc_event *, struct message *); } handlers[] = { /* Must be kept ordered. */ { "001", handle_connect }, @@ -378,21 +552,30 @@ }; static int -compare_handler(const void *d1, const void *d2) +cmp_handler(const char *name, const struct handler *handler) { - return strcmp(d1, ((const struct handler *)d2)->command); + return strcmp(name, handler->command); +} + +static inline struct handler * +find_handler(const char *name) +{ + return bsearch(name, handlers, IRC_UTIL_SIZE(handlers), sizeof (struct handler), + (irc_cmp)(cmp_handler)); } static void -handle(struct irc_server *s, struct irc_event *ev) +handle(struct irc_server *s, struct irc_event *ev, struct message *msg) { - const struct handler *c = bsearch(ev->msg.cmd, handlers, IRC_UTIL_SIZE(handlers), - sizeof (*c), &(compare_handler)); + const struct handler *h; - if (c) { - ev->server = s; - c->handle(s, ev); - } + if (!(h = find_handler(msg->cmd))) + return; + + memset(ev, 0, sizeof (*ev)); + + ev->server = s; + h->handle(s, ev, msg); } static void @@ -465,18 +648,14 @@ assert(s); - int r; - if (!(s->flags & IRC_SERVER_FLAGS_SSL)) return; - switch ((r = SSL_get_error(s->ssl, ret))) { + switch (SSL_get_error(s->ssl, ret)) { case SSL_ERROR_WANT_READ: - printf("new ssl state: %d\n", s->ssl_state); s->ssl_state = IRC_SERVER_SSL_NEED_READ; break; case SSL_ERROR_WANT_WRITE: - printf("new ssl state: %d\n", s->ssl_state); s->ssl_state = IRC_SERVER_SSL_NEED_WRITE; break; case SSL_ERROR_SSL: @@ -538,7 +717,8 @@ { int cflags = 0; - if ((cflags = fcntl(s->fd, F_GETFL)) < 0 || fcntl(s->fd, F_SETFL, cflags | O_NONBLOCK) < 0) + if ((cflags = fcntl(s->fd, F_GETFL)) < 0 || + fcntl(s->fd, F_SETFL, cflags | O_NONBLOCK) < 0) return false; return true; @@ -812,6 +992,7 @@ assert(s); assert(ev); + struct message msg; char *pos; size_t length; @@ -822,12 +1003,8 @@ *pos = 0; length = pos - s->in; - /* Clear event in case we don't understand this message. */ - memset(ev, 0, sizeof (*ev)); - ev->type = IRC_EVENT_UNKNOWN; - - if (length > 0 && irc_event_parse(&ev->msg, s->in)) - handle(s, ev); + if (length > 0 && parse(&msg, s->in)) + handle(s, ev, &msg); memmove(s->in, pos + 2, sizeof (s->in) - (length + 2)); @@ -1028,13 +1205,36 @@ assert(s); assert(target); +#if 0 /* Cleanup previous result. */ free(s->whois.channels); memset(&s->whois, 0, sizeof (s->whois)); +#endif return irc_server_send(s, "WHOIS %s", target); } +struct irc_server_namemode +irc_server_strip(const struct irc_server *s, const char *item) +{ + assert(s); + assert(item); + + struct irc_server_namemode ret = {0}; + + for (size_t i = 0; i < IRC_UTIL_SIZE(s->prefixes); ++i) { + if (item[0] == s->prefixes[i].token) { + ret.mode = s->prefixes[i].mode; + ++item; + break; + } + } + + ret.name = (char *)item; + + return ret; +} + void irc_server_incref(struct irc_server *s) { @@ -1051,7 +1251,7 @@ if (--s->refc == 0) { clear(s); - free(s->whois.channels); + //free(s->whois.channels); free(s->channels); free(s); }
--- a/lib/irccd/server.h Thu Jan 21 09:49:42 2021 +0100 +++ b/lib/irccd/server.h Thu Jan 21 15:34:25 2021 +0100 @@ -28,12 +28,12 @@ # include <openssl/ssl.h> #endif +#include "event.h" #include "limits.h" struct pollfd; struct irc_channel; -struct irc_event; enum irc_server_state { IRC_SERVER_STATE_DISCONNECTED, @@ -67,16 +67,10 @@ #endif -struct irc_server_whois { - char nickname[IRC_NICKNAME_MAX]; - char username[IRC_USERNAME_MAX]; - char realname[IRC_REALNAME_MAX]; - char hostname[IRC_HOST_MAX]; - struct { - char channel[IRC_CHANNEL_MAX]; - char mode; - } *channels; - size_t channelsz; +struct irc_server_namemode { + char mode; + char sym; + char *name; }; struct irc_server { @@ -115,8 +109,7 @@ enum irc_server_ssl_state ssl_state; #endif - /* Whois being stored. */ - struct irc_server_whois whois; + struct irc_event_whois bufwhois; /* Reference count. */ size_t refc; @@ -190,6 +183,9 @@ bool irc_server_whois(struct irc_server *, const char *); +struct irc_server_namemode +irc_server_strip(const struct irc_server *, const char *); + void irc_server_incref(struct irc_server *);
--- a/lib/irccd/util.h Thu Jan 21 09:49:42 2021 +0100 +++ b/lib/irccd/util.h Thu Jan 21 15:34:25 2021 +0100 @@ -23,6 +23,9 @@ #define IRC_UTIL_SIZE(x) (sizeof (x) / sizeof (x[0])) +/* Suitable convenient typedef for bsearch/qsort. */ +typedef int (*irc_cmp)(const void *, const void *); + void * irc_util_malloc(size_t);