# HG changeset patch # User David Demelier # Date 1612704988 -3600 # Node ID 342fb90f25123af986bee742489daac9451e1bd4 # Parent e2a86096bc0538d029a5d963e6a0dc0ec0fa0eb5 irccdctl: re-implement many of the plugin-* commands diff -r e2a86096bc05 -r 342fb90f2512 CHANGES.md --- a/CHANGES.md Wed Feb 03 20:45:00 2021 +0100 +++ b/CHANGES.md Sun Feb 07 14:36:28 2021 +0100 @@ -15,6 +15,12 @@ mode changes. It is now more convenient from the plugins to quickly inspect if someone is present on a channel. +irccdctl: + +- Commands `plugin-reload` and `plugin-unload` can be invoked without arguments. +- New `plugin-template` and `plugin-path` command which are synonyms of + `plugin-config` but for templates and paths respectively. + misc: - Split irccd-api manual page into individual irccd-api- for a better @@ -26,6 +32,10 @@ - Network protocol uses plain text again. - Transport uses clear UNIX sockets only without passwords. +javascript API: + +- Brand new Irccd.Rule API to inspect and manage rules. + irccd 3.1.1 2021-01-04 ---------------------- diff -r e2a86096bc05 -r 342fb90f2512 MIGRATING.md --- a/MIGRATING.md Wed Feb 03 20:45:00 2021 +0100 +++ b/MIGRATING.md Sun Feb 07 14:36:28 2021 +0100 @@ -17,7 +17,12 @@ Irccdctl -------- -The `irccdctl.conf` is now using a custom syntax. +- There is no longer configuration file because it now use a plain UNIX socket + to */tmp/irccd.sock* by default (may be changed through the `-s` option). +- Aliases have been removed, please use shell scripts or aliases instead. +- The `watch` command no longer produce JSON output but only the original + "human" format but may be used for scripts as it is honored through the + semantic versioning. Platform support ---------------- diff -r e2a86096bc05 -r 342fb90f2512 irccd/js-plugin.c --- a/irccd/js-plugin.c Wed Feb 03 20:45:00 2021 +0100 +++ b/irccd/js-plugin.c Sun Feb 07 14:36:28 2021 +0100 @@ -141,27 +141,24 @@ static const char ** get_table(duk_context *ctx, const char *name, char ***ptable) { - char **list; - size_t listsz; + char **list = NULL; + size_t listsz = 0; duk_get_global_string(ctx, name); - - if (!(listsz = duk_get_length(ctx, -1))) { - duk_pop(ctx); - return NULL; - } - - list = irc_util_calloc(listsz + 1, sizeof (char *)); - duk_enum(ctx, -1, 0); - for (size_t i = 0; i < listsz && duk_next(ctx, -1, 1); ++i) { + for (size_t i = 0; duk_next(ctx, -1, 1); ++i) { + list = irc_util_reallocarray(list, ++listsz, sizeof (char *)); list[i] = irc_util_strdup(duk_to_string(ctx, -2)); duk_pop_n(ctx, 2); } duk_pop_n(ctx, 2); + /* Add a NULL sentinel value. */ + list = irc_util_reallocarray(list, listsz + 1, sizeof (char *)); + list[listsz] = NULL; + freelist(*ptable); *ptable = list; diff -r e2a86096bc05 -r 342fb90f2512 irccd/main.c --- a/irccd/main.c Wed Feb 03 20:45:00 2021 +0100 +++ b/irccd/main.c Sun Feb 07 14:36:28 2021 +0100 @@ -46,7 +46,7 @@ }; static const char *config = IRCCD_SYSCONFDIR "/irccd.conf"; -static struct peers peers; +static struct peers peers = LIST_HEAD_INITIALIZER(); static int running = 1; /* conf.y */ @@ -118,10 +118,9 @@ flush(const struct pollables *pb) { struct peer *peer, *tmp; - struct pollfd *fd = pb->fds + pb->botsz; + struct pollfd *fd = pb->fds + pb->botsz + 1; irc_bot_flush(pb->fds); - transport_flush(fd++); LIST_FOREACH_SAFE(peer, &peers, link, tmp) { if (peer_flush(peer, fd++) < 0) { @@ -129,6 +128,13 @@ peer_finish(peer); } } + + /* + * Add a new client only now because we would iterate over a list + * of pollfd that is smaller than the client list. + */ + if ((peer = transport_flush(pb->fds + pb->botsz))) + LIST_INSERT_HEAD(&peers, peer, link); } static inline void diff -r e2a86096bc05 -r 342fb90f2512 irccd/peer.c --- a/irccd/peer.c Wed Feb 03 20:45:00 2021 +0100 +++ b/irccd/peer.c Sun Feb 07 14:36:28 2021 +0100 @@ -35,6 +35,10 @@ #include "peer.h" +typedef void (*plugin_set_fn)(struct irc_plugin *, const char *, const char *); +typedef const char * (*plugin_get_fn)(struct irc_plugin *, const char *); +typedef const char ** (*plugin_list_fn)(struct irc_plugin *); + static size_t parse(char *line, const char **args, size_t max) { @@ -63,7 +67,7 @@ return idx; } -static struct irc_server * +static inline struct irc_server * require_server(struct peer *p, const char *id) { struct irc_server *s; @@ -76,6 +80,19 @@ return s; } +static inline struct irc_plugin * +require_plugin(struct peer *p, const char *id) +{ + struct irc_plugin *plg; + + if (!(plg = irc_bot_plugin_get(id))) { + peer_send(p, "plugin %s not found", id); + return NULL; + } + + return plg; +} + static int ok(struct peer *p) { @@ -84,8 +101,186 @@ return 0; } +static int +plugin_list_set(struct peer *p, + char *line, + plugin_set_fn set, + plugin_get_fn get, + plugin_list_fn list) +{ + const char *args[3] = {0}, *value, **keys; + char out[IRC_BUF_LEN]; + FILE *fp; + struct irc_plugin *plg; + size_t argsz, keysz = 0; + + if ((argsz = parse(line, args, 3)) < 1) + return EINVAL; + if (!(plg = require_plugin(p, args[0]))) + return 0; + + fp = fmemopen(out, sizeof (out) - 1, "w"); + + if (argsz == 3) { + set(plg, args[1], args[2]); + fprintf(fp, "OK"); + } else if (argsz == 2) { + if ((value = get(plg, args[1]))) + fprintf(fp, "OK 1\n%s", value); + else + fprintf(fp, "ERROR key not found"); + } else { + keys = list(plg); + + /* Compute the number of keys found. */ + for (const char **key = keys; key && *key; ++key) + keysz++; + + fprintf(fp, "OK %zu\n", keysz); + + for (const char **key = keys; key && *key; ++key) { + value = get(plg, *key); + fprintf(fp, "%s=%s\n", *key, value ? value : ""); + } + } + + fclose(fp); + peer_send(p, out); + + return 0; +} + /* - * DISCONNECT [server] + * PLUGIN-CONFIG plugin [var [value]] + */ +static int +cmd_plugin_config(struct peer *p, char *line) +{ + return plugin_list_set(p, line, + irc_plugin_set_option, irc_plugin_get_option, irc_plugin_get_options); +} + +/* + * PLUGIN-INFO plugin + */ +static int +cmd_plugin_info(struct peer *p, char *line) +{ + struct irc_plugin *plg; + const char *args[1]; + + if (parse(line, args, 1) != 1) + return EINVAL; + if (!(plg = require_plugin(p, args[0]))) + return 0; + + peer_send(p, "OK %s\n%s\n%s\n%s\n%s", plg->name, plg->description, + plg->version, plg->license, plg->author); + + return 0; +} + +/* + * PLUGIN-LOAD plugin + */ +static int +cmd_plugin_load(struct peer *p, char *line) +{ + struct irc_plugin *plg; + + if (!(plg = irc_bot_plugin_find(line, NULL))) + peer_send(p, "could not load plugin: %s", strerror(errno)); + + /* TODO: report error if fails to open. */ + irc_bot_plugin_add(plg); + + return ok(p); +} + +/* + * PLUGIN-PATH plugin [var [value]] + */ +static int +cmd_plugin_path(struct peer *p, char *line) +{ + return plugin_list_set(p, line, + irc_plugin_set_path, irc_plugin_get_path, irc_plugin_get_paths); +} + +/* + * PLUGIN-LIST + */ +static int +cmd_plugin_list(struct peer *p, char *line) +{ + (void)line; + + struct irc_plugin *plg; + FILE *fp; + char out[IRC_BUF_LEN]; + + fp = fmemopen(out, sizeof (out) - 1, "w"); + + fprintf(fp, "OK "); + + LIST_FOREACH(plg, &irc.plugins, link) { + fprintf(fp, "%s", plg->name); + + if (LIST_NEXT(plg, link)) + fputc(' ', fp); + } + + fclose(fp); + peer_send(p, out); + + return 0; +} + +/* + * PLUGIN-RELOAD plugin + */ +static int +cmd_plugin_reload(struct peer *p, char *line) +{ + struct irc_plugin *plg; + + if (!(plg = irc_bot_plugin_get(line))) + peer_send(p, "could not reload plugin: %s", strerror(errno)); + + /* TODO: report error if fails to reload. */ + + return ok(p); +} + +/* + * PLUGIN-TEMPLATE plugin [var [value]] + */ +static int +cmd_plugin_template(struct peer *p, char *line) +{ + return plugin_list_set(p, line, + irc_plugin_set_template, irc_plugin_get_template, irc_plugin_get_templates); +} + +/* + * PLUGIN-UNLOAD [plugin] + */ +static int +cmd_plugin_unload(struct peer *p, char *line) +{ + const char *args[1] = {0}; + + /* TODO report error if plugin not found. */ + if (parse(line, args, 1) == 0) + irc_bot_plugin_clear(); + else + irc_bot_plugin_remove(args[0]); + + return ok(p); +} + +/* + * SERVER-DISCONNECT [server] */ static int cmd_server_disconnect(struct peer *p, char *line) @@ -105,7 +300,7 @@ } /* - * MESSAGE server channel message + * SERVER-MESSAGE server channel message */ static int cmd_server_message(struct peer *p, char *line) @@ -124,7 +319,7 @@ } /* - * ME server channel message + * SERVER-ME server channel message */ static int cmd_server_me(struct peer *p, char *line) @@ -143,7 +338,7 @@ } /* - * MODE server channel mode [limit] [user] [mask] + * SERVER-MODE server channel mode [limit] [user] [mask] */ static int cmd_server_mode(struct peer *p, char *line) @@ -166,7 +361,7 @@ } /* - * NOTICE server channel message + * SERVER-NOTICE server channel message */ static int cmd_server_notice(struct peer *p, char *line) @@ -185,7 +380,44 @@ } /* - * INVITE server channel target + * SERVER-INFO server + */ +static int +cmd_server_info(struct peer *p, char *line) +{ + const char *args[1] = {0}; + const struct irc_server *s; + const struct irc_channel *c; + char out[IRC_BUF_LEN]; + FILE *fp; + + if (parse(line, args, 1) != 1) + return EINVAL; + if (!(s = require_server(p, args[0]))) + return 0; + + fp = fmemopen(out, sizeof (out) - 1, "w"); + + fprintf(fp, "OK %s\n", s->name); + fprintf(fp, "%s %u%s\n", s->conn.hostname, s->conn.port, + s->flags & IRC_SERVER_FLAGS_SSL ? " ssl" : ""); + fprintf(fp, "%s %s %s\n", s->ident.nickname, s->ident.username, s->ident.realname); + + LIST_FOREACH(c, &s->channels, link) { + fprintf(fp, "%s", c->name); + + if (LIST_NEXT(c, link)) + fputc(' ', fp); + } + + fclose(fp); + peer_send(p, out); + + return 0; +} + +/* + * SERVER-INVITE server channel target */ static int cmd_server_invite(struct peer *p, char *line) @@ -204,7 +436,7 @@ } /* - * JOIN server channel [password] + * SERVER-JOIN server channel [password] */ static int cmd_server_join(struct peer *p, char *line) @@ -217,13 +449,13 @@ if (!(s = require_server(p, args[0]))) return 0; - irc_server_join(s, args[1], args[2][0] ? args[2] : NULL); + irc_server_join(s, args[1], args[2] ? args[2] : NULL); return ok(p); } /* - * KICK server channel target [reason] + * SERVER-KICK server channel target [reason] */ static int cmd_server_kick(struct peer *p, char *line) @@ -241,6 +473,9 @@ return ok(p); } +/* + * SERVER-LIST + */ static int cmd_server_list(struct peer *p, char *line) { @@ -270,7 +505,7 @@ } /* - * PART server channel [reason] + * SERVER-PART server channel [reason] */ static int cmd_server_part(struct peer *p, char *line) @@ -289,7 +524,7 @@ } /* - * TOPIC server channel topic + * SERVER-TOPIC server channel topic */ static int cmd_server_topic(struct peer *p, char *line) @@ -321,7 +556,16 @@ const char *name; int (*call)(struct peer *, char *); } cmds[] = { + { "PLUGIN-CONFIG", cmd_plugin_config }, + { "PLUGIN-INFO", cmd_plugin_info }, + { "PLUGIN-LIST", cmd_plugin_list }, + { "PLUGIN-LOAD", cmd_plugin_load }, + { "PLUGIN-PATH", cmd_plugin_path }, + { "PLUGIN-RELOAD", cmd_plugin_reload }, + { "PLUGIN-TEMPLATE", cmd_plugin_template }, + { "PLUGIN-UNLOAD", cmd_plugin_unload }, { "SERVER-DISCONNECT", cmd_server_disconnect }, + { "SERVER-INFO", cmd_server_info }, { "SERVER-INVITE", cmd_server_invite }, { "SERVER-JOIN", cmd_server_join }, { "SERVER-KICK", cmd_server_kick }, @@ -336,18 +580,16 @@ }; static int -cmp_cmd(const void *d1, const void *d2) +cmp_cmd(const char *key, const struct cmd *cmd) { - const char *key = d1; - const struct cmd *cmd = d2; - return strncmp(key, cmd->name, strlen(cmd->name)); } static const struct cmd * find(const char *line) { - return bsearch(line, cmds, IRC_UTIL_SIZE(cmds), sizeof (struct cmd), cmp_cmd); + return bsearch(line, cmds, IRC_UTIL_SIZE(cmds), + sizeof (cmds[0]), (irc_cmp)cmp_cmd); } static void diff -r e2a86096bc05 -r 342fb90f2512 irccdctl/main.c --- a/irccdctl/main.c Wed Feb 03 20:45:00 2021 +0100 +++ b/irccdctl/main.c Sun Feb 07 14:36:28 2021 +0100 @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -125,13 +126,365 @@ } } -static void +static char * ok(void) { - const char *response = poll(); + char *response = poll(); + + if (strncmp(response, "OK", 2) != 0) + errx(1, "abort: %s", response); + + /* Skip "OK". */ + response += 2; + + while (*response && isspace(*response)) + response++; + + return response; +} + +static void +show_connect(char *line) +{ + const char *args[2] = {0}; + + if (irc_util_split(line, args, 2) == 2) { + printf("%-16s: %s\n", "event", "onConnect"); + printf("%-16s: %s\n", "server", args[0]); + } +} + +static void +show_disconnect(char *line) +{ + const char *args[2] = {0}; + + if (irc_util_split(line, args, 2) == 2) { + printf("%-16s: %s\n", "event", "onDisonnect"); + printf("%-16s: %s\n", "server", args[0]); + } +} + +static void +show_invite(char *line) +{ + const char *args[5] = {0}; + + if (irc_util_split(line, args, 5) == 5) { + printf("%-16s: %s\n", "event", "onInvite"); + printf("%-16s: %s\n", "server", args[1]); + printf("%-16s: %s\n", "origin", args[2]); + printf("%-16s: %s\n", "channel", args[3]); + printf("%-16s: %s\n", "nickname", args[4]); + } +} + +static void +show_join(char *line) +{ + const char *args[4] = {0}; + + if (irc_util_split(line, args, 4) == 4) { + printf("%-16s: %s\n", "event", "onJoin"); + printf("%-16s: %s\n", "server", args[1]); + printf("%-16s: %s\n", "origin", args[2]); + printf("%-16s: %s\n", "channel", args[3]); + } +} + +static void +show_kick(char *line) +{ + const char *args[6] = {0}; + + if (irc_util_split(line, args, 6) >= 5) { + printf("%-16s: %s\n", "event", "onKick"); + printf("%-16s: %s\n", "server", args[1]); + printf("%-16s: %s\n", "origin", args[2]); + printf("%-16s: %s\n", "channel", args[3]); + printf("%-16s: %s\n", "target", args[4]); + printf("%-16s: %s\n", "reason", args[5] ? args[5] : ""); + } +} + +static void +show_me(char *line) +{ + const char *args[5] = {0}; + + if (irc_util_split(line, args, 5) == 5) { + printf("%-16s: %s\n", "event", "onMe"); + printf("%-16s: %s\n", "server", args[1]); + printf("%-16s: %s\n", "origin", args[2]); + printf("%-16s: %s\n", "channel", args[3]); + printf("%-16s: %s\n", "message", args[4]); + } +} + +static void +show_message(char *line) +{ + const char *args[5] = {0}; + + if (irc_util_split(line, args, 5) == 5) { + printf("%-16s: %s\n", "event", "onMessage"); + printf("%-16s: %s\n", "server", args[1]); + printf("%-16s: %s\n", "origin", args[2]); + printf("%-16s: %s\n", "channel", args[3]); + printf("%-16s: %s\n", "message", args[4]); + } +} + +static void +show_mode(char *line) +{ + const char *args[8] = {0}; + + if (irc_util_split(line, args, 8) >= 5) { + printf("%-16s: %s\n", "event", "onMode"); + printf("%-16s: %s\n", "server", args[1]); + printf("%-16s: %s\n", "origin", args[2]); + printf("%-16s: %s\n", "channel", args[3]); + printf("%-16s: %s\n", "mode", args[4]); + printf("%-16s: %s\n", "limit", (args[5] ? args[5] : "")); + printf("%-16s: %s\n", "user", (args[6] ? args[6] : "")); + printf("%-16s: %s\n", "mask", (args[7] ? args[7] : "")); + } +} + +static void +show_nick(char *line) +{ + const char *args[4] = {0}; + + if (irc_util_split(line, args, 4) == 4) { + printf("%-16s: %s\n", "event", "onNick"); + printf("%-16s: %s\n", "server", args[1]); + printf("%-16s: %s\n", "origin", args[2]); + printf("%-16s: %s\n", "nickname", args[3]); + } +} + +static void +show_notice(char *line) +{ + const char *args[5] = {0}; + + if (irc_util_split(line, args, 5) == 5) { + printf("%-16s: %s\n", "event", "onNotice"); + printf("%-16s: %s\n", "server", args[1]); + printf("%-16s: %s\n", "origin", args[2]); + printf("%-16s: %s\n", "channel", args[3]); + printf("%-16s: %s\n", "message", args[4]); + } +} + +static void +show_part(char *line) +{ + const char *args[5] = {0}; + + if (irc_util_split(line, args, 5) >= 4) { + printf("%-16s: %s\n", "event", "onPart"); + printf("%-16s: %s\n", "server", args[1]); + printf("%-16s: %s\n", "origin", args[2]); + printf("%-16s: %s\n", "channel", args[3]); + printf("%-16s: %s\n", "reason", (args[4] ? args[4] : "")); + } +} + +static void +show_topic(char *line) +{ + const char *args[5] = {0}; + + if (irc_util_split(line, args, 5) >= 4) { + printf("%-16s: %s\n", "event", "onTopic"); + printf("%-16s: %s\n", "server", args[1]); + printf("%-16s: %s\n", "origin", args[2]); + printf("%-16s: %s\n", "channel", args[3]); + printf("%-16s: %s\n", "topic", args[4]); + } +} - if (strcmp(response, "OK") != 0) - errx(1, "abort: %s", response); +static void +show_whois(char *line) +{ + const char *args[6] = {0}; + //char *p, *token; + + if (irc_util_split(line, args, 6) >= 4) { + printf("%-16s: %s\n", "event", "onWhois"); + printf("%-16s: %s\n", "server", args[1]); + printf("%-16s: %s\n", "nickname", args[2]); + printf("%-16s: %s\n", "username", args[3]); + printf("%-16s: %s\n", "hostname", args[4]); + printf("%-16s: %s\n", "username", args[5]); + //printf("channels: %s\n", args[6]); + } +} + +static const struct { + const char *event; + void (*show)(char *); +} watchtable[] = { + { "EVENT-CONNECT", show_connect }, + { "EVENT-DISCONNECT", show_disconnect }, + { "EVENT-INVITE", show_invite }, + { "EVENT-JOIN", show_join }, + { "EVENT-KICK", show_kick }, + { "EVENT-MESSAGE", show_message }, + { "EVENT-ME", show_me }, + { "EVENT-MODE", show_mode }, + { "EVENT-NICK", show_nick }, + { "EVENT-NOTICE", show_notice }, + { "EVENT-PART", show_part }, + { "EVENT-TOPIC", show_topic }, + { "EVENT-WHOIS", show_whois } +}; + +static void +show(char *ev) +{ + for (size_t i = 0; i < IRC_UTIL_SIZE(watchtable); ++i) { + if (strncmp(watchtable[i].event, ev, strlen(watchtable[i].event)) == 0) { + watchtable[i].show(ev); + printf("\n"); + break; + } + } +} + +static void +plugin_list_set(int argc, char **argv, const char *cmd) +{ + char *line, *p; + size_t num = 0; + + if (argc == 3) { + req("%s %s %s %s", cmd, argv[0], argv[1], argv[2]); + ok(); + return; + } + + if (argc == 2) + req("%s %s %s", cmd, argv[0], argv[1]); + else + req("%s %s", cmd, argv[0]); + + if (sscanf(line = ok(), "%zu", &num) != 1) + errx(1, "could not retrieve list"); + + if (argc == 2) + puts(poll()); + else { + while (num-- != 0 && (line = poll())) { + if (!(p = strchr(line, '='))) + continue; + + *p = '\0'; + printf("%-16s: %s\n", line, p + 1); + } + } + +} + +static void +cmd_plugin_config(int argc, char **argv) +{ + return plugin_list_set(argc, argv, "PLUGIN-CONFIG"); +} + +/* + * Response: + * + * OK name + * summary + * version + * license + * author + */ +static void +cmd_plugin_info(int argc, char **argv) +{ + (void)argc; + + const char *response; + + req("PLUGIN-INFO %s", argv[0]); + + if (strncmp((response = poll()), "OK ", 3) != 0) + errx(1, "failed to retrieve plugin information"); + + printf("%-16s: %s\n", "name", response + 3); + printf("%-16s: %s\n", "summary", poll()); + printf("%-16s: %s\n", "version", poll()); + printf("%-16s: %s\n", "license", poll()); + printf("%-16s: %s\n", "author", poll()); +} + +static void +cmd_plugin_list(int argc, char **argv) +{ + (void)argc; + (void)argv; + + char *list; + + req("PLUGIN-LIST"); + + if (strncmp(list = poll(), "OK ", 3) != 0) + errx(1, "failed to retrieve plugin list"); + + list += 3; + + for (char *p; (p = strchr(list, ' ')); ) + *p = '\n'; + + puts(list); +} + +static void +cmd_plugin_load(int argc, char **argv) +{ + (void)argc; + + req("PLUGIN-LOAD %s", argv[0]); + ok(); +} + +static void +cmd_plugin_path(int argc, char **argv) +{ + return plugin_list_set(argc, argv, "PLUGIN-PATH"); +} + +static void +cmd_plugin_reload(int argc, char **argv) +{ + if (argc == 1) + req("PLUGIN-RELOAD %s", argv[0]); + else + req("PLUGIN-RELOAD"); + + ok(); +} + +static void +cmd_plugin_template(int argc, char **argv) +{ + return plugin_list_set(argc, argv, "PLUGIN-TEMPLATE"); +} + +static void +cmd_plugin_unload(int argc, char **argv) +{ + if (argc == 1) + req("PLUGIN-UNLOAD %s", argv[0]); + else + req("PLUGIN-UNLOAD"); + + ok(); } static void @@ -145,6 +498,58 @@ ok(); } +/* + * Response: + * + * OK name + * hostname port [ssl] + * nickname username realname + * chan1 chan2 chanN + */ +static void +cmd_server_info(int argc, char **argv) +{ + (void)argc; + + char *list; + const char *args[16] = {0}; + + req("SERVER-INFO %s", argv[0]); + + if (strncmp(list = poll(), "OK ", 3) != 0) + errx(1, "failed to retrieve server information"); + + printf("%-16s: %s\n", "name", list + 3); + + if (irc_util_split((list = poll()), args, 3) < 2) + errx(1, "malformed server connection"); + + printf("%-16s: %s\n", "hostname", args[0]); + printf("%-16s: %s\n", "port", args[1]); + + if (args[2]) + printf("%-16s: %s\n", "ssl", "true"); + + if (irc_util_split((list = poll()), args, 3) != 3) + errx(1, "malformed server ident"); + + printf("%-16s: %s\n", "nickname", args[0]); + printf("%-16s: %s\n", "username", args[0]); + printf("%-16s: %s\n", "realname", args[0]); + printf("%-16s: %s\n", "channels", poll()); +} + +static void +cmd_server_join(int argc, char **argv) +{ + if (argc >= 3) + req("SERVER-JOIN %s %s %s", argv[0], argv[1], argv[2]); + else + req("SERVER-JOIN %s %s", argv[0], argv[1]); + + ok(); +} + static void cmd_server_list(int argc, char **argv) { @@ -239,218 +644,6 @@ } static void -show_connect(char *line) -{ - const char *args[2] = {0}; - - if (irc_util_split(line, args, 2) == 2) { - printf("event: onConnect\n"); - printf("server: %s\n", args[0]); - } -} - -static void -show_disconnect(char *line) -{ - const char *args[2] = {0}; - - if (irc_util_split(line, args, 2) == 2) { - printf("event: onDisonnect\n"); - printf("server: %s\n", args[0]); - } -} - -static void -show_invite(char *line) -{ - const char *args[5] = {0}; - - if (irc_util_split(line, args, 5) == 5) { - printf("event: onInvite\n"); - printf("server: %s\n", args[1]); - printf("origin: %s\n", args[2]); - printf("channel: %s\n", args[3]); - printf("nickname: %s\n", args[4]); - } -} - -static void -show_join(char *line) -{ - const char *args[4] = {0}; - - if (irc_util_split(line, args, 4) == 4) { - printf("event: onJoin\n"); - printf("server: %s\n", args[1]); - printf("origin: %s\n", args[2]); - printf("channel: %s\n", args[3]); - } -} - -static void -show_kick(char *line) -{ - const char *args[6] = {0}; - - if (irc_util_split(line, args, 6) >= 5) { - printf("event: onKick\n"); - printf("server: %s\n", args[1]); - printf("origin: %s\n", args[2]); - printf("channel: %s\n", args[3]); - printf("target: %s\n", args[4]); - printf("reason: %s\n", args[5] ? args[5] : ""); - } -} - -static void -show_me(char *line) -{ - const char *args[5] = {0}; - - if (irc_util_split(line, args, 5) == 5) { - printf("event: onMe\n"); - printf("server: %s\n", args[1]); - printf("origin: %s\n", args[2]); - printf("channel: %s\n", args[3]); - printf("message: %s\n", args[4]); - } -} - -static void -show_message(char *line) -{ - const char *args[5] = {0}; - - if (irc_util_split(line, args, 5) == 5) { - printf("event: onMessage\n"); - printf("server: %s\n", args[1]); - printf("origin: %s\n", args[2]); - printf("channel: %s\n", args[3]); - printf("message: %s\n", args[4]); - } -} - -static void -show_mode(char *line) -{ - const char *args[8] = {0}; - - if (irc_util_split(line, args, 8) >= 5) { - printf("event: onMode\n"); - printf("server: %s\n", args[1]); - printf("origin: %s\n", args[2]); - printf("channel: %s\n", args[3]); - printf("mode: %s\n", args[4]); - printf("limit: %s\n", (args[5] ? args[5] : "")); - printf("user: %s\n", (args[6] ? args[6] : "")); - printf("mask: %s\n", (args[7] ? args[7] : "")); - } -} - -static void -show_nick(char *line) -{ - const char *args[4] = {0}; - - if (irc_util_split(line, args, 4) == 4) { - printf("event: onNick\n"); - printf("server: %s\n", args[1]); - printf("origin: %s\n", args[2]); - printf("nickname: %s\n", args[3]); - } -} - -static void -show_notice(char *line) -{ - const char *args[5] = {0}; - - if (irc_util_split(line, args, 5) == 5) { - printf("event: onNotice\n"); - printf("server: %s\n", args[1]); - printf("origin: %s\n", args[2]); - printf("channel: %s\n", args[3]); - printf("message: %s\n", args[4]); - } -} - -static void -show_part(char *line) -{ - const char *args[5] = {0}; - - if (irc_util_split(line, args, 5) >= 4) { - printf("event: onPart\n"); - printf("server: %s\n", args[1]); - printf("origin: %s\n", args[2]); - printf("channel: %s\n", args[3]); - printf("reason: %s\n", (args[4] ? args[4] : "")); - } -} - -static void -show_topic(char *line) -{ - const char *args[5] = {0}; - - if (irc_util_split(line, args, 5) >= 4) { - printf("event: onTopic\n"); - printf("server: %s\n", args[1]); - printf("origin: %s\n", args[2]); - printf("channel: %s\n", args[3]); - printf("topic: %s\n", args[4]); - } -} - -static void -show_whois(char *line) -{ - const char *args[6] = {0}; - //char *p, *token; - - if (irc_util_split(line, args, 6) >= 4) { - printf("event: onWhois\n"); - printf("server: %s\n", args[1]); - printf("nickname: %s\n", args[2]); - printf("username: %s\n", args[3]); - printf("hostname: %s\n", args[4]); - printf("username: %s\n", args[5]); - //printf("channels: %s\n", args[6]); - } -} - -static const struct { - const char *event; - void (*show)(char *); -} watchtable[] = { - { "EVENT-CONNECT", show_connect }, - { "EVENT-DISCONNECT", show_disconnect }, - { "EVENT-INVITE", show_invite }, - { "EVENT-JOIN", show_join }, - { "EVENT-KICK", show_kick }, - { "EVENT-MESSAGE", show_message }, - { "EVENT-ME", show_me }, - { "EVENT-MODE", show_mode }, - { "EVENT-NICK", show_nick }, - { "EVENT-NOTICE", show_notice }, - { "EVENT-PART", show_part }, - { "EVENT-TOPIC", show_topic }, - { "EVENT-WHOIS", show_whois } -}; - -static void -show(char *ev) -{ - for (size_t i = 0; i < IRC_UTIL_SIZE(watchtable); ++i) { - if (strncmp(watchtable[i].event, ev, strlen(watchtable[i].event)) == 0) { - watchtable[i].show(ev); - printf("\n"); - break; - } - } -} - -static void cmd_watch(int argc, char **argv) { (void)argc; @@ -478,7 +671,17 @@ void (*exec)(int, char **); } cmds[] = { /* name min max exec */ + { "plugin-config", 1, 3, cmd_plugin_config }, + { "plugin-info", 1, 1, cmd_plugin_info }, + { "plugin-list", 0, 0, cmd_plugin_list }, + { "plugin-load", 1, 1, cmd_plugin_load }, + { "plugin-path", 0, 3, cmd_plugin_path }, + { "plugin-reload", 0, 1, cmd_plugin_reload }, + { "plugin-template", 1, 3, cmd_plugin_template }, + { "plugin-unload", 0, 1, cmd_plugin_unload }, { "server-disconnect", 0, 1, cmd_server_disconnect }, + { "server-info", 1, 1, cmd_server_info }, + { "server-join", 2, 3, cmd_server_join }, { "server-list", 0, 0, cmd_server_list }, { "server-me", 3, 3, cmd_server_me }, { "server-message", 3, 3, cmd_server_message }, diff -r e2a86096bc05 -r 342fb90f2512 lib/irccd/limits.h --- a/lib/irccd/limits.h Wed Feb 03 20:45:00 2021 +0100 +++ b/lib/irccd/limits.h Sun Feb 07 14:36:28 2021 +0100 @@ -31,6 +31,8 @@ #define IRC_USERMODES_LEN 8 /* Number of modes (e.g. ohv). */ #define IRC_CHANTYPES_LEN 8 /* Channel types. */ #define IRC_CMDCHAR_LEN 4 /* Prefix for plugin commands (e.g. !). */ +#define IRC_CHARSET_LEN 16 /* Charset name max. */ +#define IRC_CASEMAPPING_LEN 16 /* Maximum case mapping length. */ #define IRC_MESSAGE_LEN 512 /* Official length per message. */ #define IRC_ARGS_MAX 32 /* Own supported number of arguments per message. */ diff -r e2a86096bc05 -r 342fb90f2512 lib/irccd/server.c --- a/lib/irccd/server.c Wed Feb 03 20:45:00 2021 +0100 +++ b/lib/irccd/server.c Sun Feb 07 14:36:28 2021 +0100 @@ -106,8 +106,8 @@ static void remove_channel(struct irc_channel *ch) { + LIST_REMOVE(ch, link); irc_channel_finish(ch); - LIST_REMOVE(ch, link); } static int @@ -156,10 +156,6 @@ for (size_t i = 0; i < IRC_UTIL_SIZE(s->params.prefixes) && *pm && *tk; ++i) { s->params.prefixes[i].mode = *pm++; s->params.prefixes[i].token = *tk++; - - irc_log_info("server %s: supports prefix %c=%c", s->name, - s->params.prefixes[i].mode, - s->params.prefixes[i].token); } } } @@ -168,7 +164,6 @@ read_support_chantypes(struct irc_server *s, const char *value) { strlcpy(s->params.chantypes, value, sizeof (s->params.chantypes)); - irc_log_info("server %s: supports channel types: %s", s->name, s->params.chantypes); } static void @@ -194,6 +189,7 @@ s->state = IRC_SERVER_STATE_NONE; ev->type = IRC_EVENT_DISCONNECT; ev->server = s; + irc_log_info("server %s: connection lost", s->name); } @@ -209,10 +205,44 @@ if (sscanf(msg->args[i], "%63[^=]=%63s", key, value) != 2) continue; - if (strcmp(key, "PREFIX") == 0) + if (strcmp(key, "PREFIX") == 0) { read_support_prefix(s, value); - if (strcmp(key, "CHANTYPES") == 0) + irc_log_info("server %s: prefixes: %s", + s->name, value); + } else if (strcmp(key, "CHANTYPES") == 0) { read_support_chantypes(s, value); + irc_log_info("server %s: channel types: %s", + s->name, value); + } else if (strcmp(key, "CHANNELLEN") == 0) { + s->params.chanlen = atoi(value); + irc_log_info("server %s: channel name limit: %u", + s->name, s->params.chanlen); + } else if (strcmp(key, "NICKLEN") == 0) { + s->params.nicklen = atoi(value); + irc_log_info("server %s: nickname limit: %u", + s->name, s->params.nicklen); + } else if (strcmp(key, "TOPICLEN") == 0) { + s->params.topiclen = atoi(value); + irc_log_info("server %s: topic limit: %u", + s->name, s->params.topiclen); + } else if (strcmp(key, "AWAYLEN") == 0) { + s->params.awaylen = atoi(value); + irc_log_info("server %s: away message limit: %u", + s->name, s->params.awaylen); + } else if (strcmp(key, "KICKLEN") == 0) { + s->params.kicklen = atoi(value); + irc_log_info("server %s: kick reason limit: %u", + s->name, s->params.kicklen); + } + else if (strcmp(key, "CHARSET") == 0) { + strlcpy(s->params.charset, value, sizeof (s->params.charset)); + irc_log_info("server %s: charset: %s", + s->name, s->params.charset); + } else if (strcmp(key, "CASEMAPPING") == 0) { + strlcpy(s->params.casemapping, value, sizeof (s->params.casemapping)); + irc_log_info("server %s: case mapping: %s", + s->name, s->params.casemapping); + } } } @@ -299,7 +329,7 @@ ch = add_channel(s, ev->part.channel, NULL, 1); - if (is_self(s, ev->part.origin) == 0) { + if (is_self(s, ev->part.origin)) { remove_channel(ch); irc_log_info("server %s: leaving channel %s", s->name, ev->part.channel); } else diff -r e2a86096bc05 -r 342fb90f2512 lib/irccd/server.h --- a/lib/irccd/server.h Wed Feb 03 20:45:00 2021 +0100 +++ b/lib/irccd/server.h Sun Feb 07 14:36:28 2021 +0100 @@ -64,6 +64,13 @@ struct irc_server_params { char chantypes[IRC_CHANTYPES_LEN]; + char charset[IRC_CHARSET_LEN]; + char casemapping[IRC_CASEMAPPING_LEN]; + unsigned int chanlen; + unsigned int nicklen; + unsigned int topiclen; + unsigned int awaylen; + unsigned int kicklen; struct { char mode; /* Mode (e.g. ov). */ char token; /* Symbol used (e.g. @+). */