# HG changeset patch # User David Demelier # Date 1610786733 -3600 # Node ID 95201fd9ad88e74b8d5f1a2f92228109916b6449 # Parent 2ec05b9db2ee0207deb33517559ab0fe462c85e2 irccd: servers are now linked lists - Add reference counting to be shared with Javascript. - Implement server-disconnect command. diff -r 2ec05b9db2ee -r 95201fd9ad88 MIGRATING.md --- a/MIGRATING.md Fri Jan 15 14:44:52 2021 +0100 +++ b/MIGRATING.md Sat Jan 16 09:45:33 2021 +0100 @@ -53,6 +53,15 @@ - The method `Util.ticks` as been removed. +### Module Server + +- The property `channels` in the object returned from `Server.info` is now an + array of objects which also contain a list of nicknames present in the + channel. +- The property `channels` in the object for the `Server` constructor now takes + an array of objects containing two properties each: `name` and `password` + which must be string (password is optional). + Migrating from 2.x to 3.x ========================= diff -r 2ec05b9db2ee -r 95201fd9ad88 irccd/main.c --- a/irccd/main.c Fri Jan 15 14:44:52 2021 +0100 +++ b/irccd/main.c Sat Jan 16 09:45:33 2021 +0100 @@ -20,8 +20,12 @@ #include #include +#include #include +#include #include +#include +#include int main(int argc, char **argv) @@ -33,11 +37,17 @@ .hostname = "malikania.fr", .port = 6667 }; - - irc_server_join(&s, "#test", NULL); + struct irc_plugin p = { + .name = "fuck" + }; irc_log_set_verbose(true); irc_bot_init(); - irc_bot_add_server(&s); + + irc_transport_bind("/tmp/irccd.sock"); + irc_server_join(&s, "#test", NULL); + irc_js_plugin_open(&p, "test.js"); + irc_bot_add_server(irc_util_memdup(&s, sizeof (s))); + irc_bot_add_plugin(&p); irc_bot_run(); } diff -r 2ec05b9db2ee -r 95201fd9ad88 irccdctl/main.c --- a/irccdctl/main.c Fri Jan 15 14:44:52 2021 +0100 +++ b/irccdctl/main.c Sat Jan 16 09:45:33 2021 +0100 @@ -132,6 +132,17 @@ } static void +cmd_server_disconnect(int argc, char **argv) +{ + if (argc == 1) + req("SERVER-DISCONNECT %s", argv[0]); + else + req("SERVER-DISCONNECT"); + + ok(); +} + +static void cmd_server_list(int argc, char **argv) { (void)argc; @@ -221,6 +232,7 @@ void (*exec)(int, char **); } cmds[] = { /* name min max exec */ + { "server-disconnect", 0, 1, cmd_server_disconnect }, { "server-list", 0, 0, cmd_server_list }, { "server-me", 3, 3, cmd_server_me }, { "server-message", 3, 3, cmd_server_message }, diff -r 2ec05b9db2ee -r 95201fd9ad88 lib/irccd/channel.c --- a/lib/irccd/channel.c Fri Jan 15 14:44:52 2021 +0100 +++ b/lib/irccd/channel.c Sat Jan 16 09:45:33 2021 +0100 @@ -46,6 +46,9 @@ assert(ch); assert(nick); + if (find(ch, nick)) + return; + struct irc_channel_user u = {0}; strlcpy(u.nickname, nick, sizeof (u.nickname)); diff -r 2ec05b9db2ee -r 95201fd9ad88 lib/irccd/event.h --- a/lib/irccd/event.h Fri Jan 15 14:44:52 2021 +0100 +++ b/lib/irccd/event.h Sat Jan 16 09:45:33 2021 +0100 @@ -50,6 +50,8 @@ /* * Raw arguments. * [0]: prefix + * [1]: origin + * [2 to argsz]: arguments */ char args[IRC_ARGS_MAX][IRC_MESSAGE_MAX]; size_t argsz; @@ -101,6 +103,10 @@ } nick; struct { + struct irc_channel *channel; + } names; + + struct { char *origin; char *channel; char *message; diff -r 2ec05b9db2ee -r 95201fd9ad88 lib/irccd/irccd.c --- a/lib/irccd/irccd.c Fri Jan 15 14:44:52 2021 +0100 +++ b/lib/irccd/irccd.c Sat Jan 16 09:45:33 2021 +0100 @@ -89,9 +89,8 @@ for (size_t p = 0; p < irc.peersz; ++p) irc_peer_prepare(&irc.peers[p], &pkg.fds[i++]); - - for (size_t s = 0; s < irc.serversz; ++s) - irc_server_prepare(&irc.servers[s], &pkg.fds[i++]); + for (struct irc_server *s = irc.servers; s; s = s->next) + irc_server_prepare(s, &pkg.fds[i++]); return pkg; } @@ -134,8 +133,13 @@ pipe_flush(&pkg->fds[i]); +#if 0 for (size_t s = 0; s < irc.serversz; ++s) - irc_server_flush(&irc.servers[s], &pkg->fds[i]); + irc_server_flush(irc.servers[s], &pkg->fds[i]); +#endif + for (struct irc_server *s = irc.servers; s; s = s->next) + irc_server_flush(s, &pkg->fds[i]); + /* Accept new transport client. */ if (irc_transport_flush(&pkg->fds[i], &peer)) @@ -155,10 +159,10 @@ * For every server, poll any kind of new event and pass them to the * plugin unless the rules explicitly disallow us to do so. */ - for (size_t s = 0; s < irc.serversz; ++s) { + for (struct irc_server *s = irc.servers; s; s = s->next) { struct irc_event ev; - while (irc_server_poll(&irc.servers[s], &ev)) + while (irc_server_poll(s, &ev)) invoke(&ev); } } @@ -179,28 +183,34 @@ } void -irc_bot_add_server(const struct irc_server *s) +irc_bot_add_server(struct irc_server *s) { assert(s); - IRC_SET_ALLOC_PUSH(&irc.servers, &irc.serversz, s, cmp_server); - irc_server_connect(&irc.servers[irc.serversz - 1]); + irc_server_incref(s); + irc_server_connect(s); + + s->next = irc.servers; + irc.servers = s; + irc.serversz++; } struct irc_server * irc_bot_find_server(const char *name) { - struct irc_server key = {0}; + struct irc_server *s; - strlcpy(key.name, name, sizeof (key.name)); + for (s = irc.servers; s; s = s->next) + if (strcmp(s->name, name) == 0) + return s; - return IRC_SET_FIND(irc.servers, irc.serversz, &key, cmp_server); + return NULL; } void irc_bot_remove_server(const char *name) { - struct irc_server *s; + struct irc_server *s, *p; if (!(s = irc_bot_find_server(name))) return; @@ -213,8 +223,35 @@ .server = s }); - /* Finally remove from array. */ - IRC_SET_ALLOC_REMOVE(&irc.servers, &irc.serversz, s); + if (s == irc.servers) + irc.servers = irc.servers->next; + else { + /* x -> y -> z */ + /* ^ */ + /* s */ + for (p = irc.servers->next; p->next != s; p = p->next) + continue; + + p->next = s->next; + } + + irc_server_decref(s); + irc.serversz--; +} + +void +irc_bot_clear_servers(void) +{ + struct irc_server *s, *next; + + if (!(s = irc.servers)) + return; + + while (s) { + next = s->next; + irc_bot_remove_server(s->name); + s = next; + } } void diff -r 2ec05b9db2ee -r 95201fd9ad88 lib/irccd/irccd.h --- a/lib/irccd/irccd.h Fri Jan 15 14:44:52 2021 +0100 +++ b/lib/irccd/irccd.h Sat Jan 16 09:45:33 2021 +0100 @@ -24,13 +24,12 @@ #include "rule.h" +#define IRC_BOT_RULE_MAX 256 + struct irc_server; struct irc_plugin; struct irc_peer; - -#define IRC_BOT_RULE_MAX 256 - extern struct irc { struct irc_peer *peers; size_t peersz; @@ -46,7 +45,7 @@ irc_bot_init(void); void -irc_bot_add_server(const struct irc_server *); +irc_bot_add_server(struct irc_server *); struct irc_server * irc_bot_find_server(const char *); @@ -55,6 +54,9 @@ irc_bot_remove_server(const char *); void +irc_bot_clear_servers(void); + +void irc_bot_add_plugin(const struct irc_plugin *); struct irc_plugin * diff -r 2ec05b9db2ee -r 95201fd9ad88 lib/irccd/js-plugin.c --- a/lib/irccd/js-plugin.c Fri Jan 15 14:44:52 2021 +0100 +++ b/lib/irccd/js-plugin.c Sat Jan 16 09:45:33 2021 +0100 @@ -26,6 +26,7 @@ #include +#include "channel.h" #include "event.h" #include "js-plugin.h" #include "jsapi-file.h" @@ -82,6 +83,17 @@ return ret ? ret : irc_util_strdup("unknown"); } +static void +push_names(duk_context *ctx, const struct irc_channel *ch) +{ + duk_push_array(ctx); + + for (size_t i = 0; i < ch->usersz; ++i) { + duk_push_string(ctx, ch->users[i].nickname); + duk_put_prop_index(ctx, -2, i); + } +} + static const char ** get_table(duk_context *ctx, const char *name, char ***ptable) { @@ -207,73 +219,52 @@ } static void -vcall(duk_context *ctx, const char *function, const char *fmt, va_list ap) +vcall(struct irc_plugin *plg, const char *function, const char *fmt, va_list ap) { + struct self *self = plg->data; int nargs = 0; - printf("obtain %s\n", function); - duk_get_global_string(ctx, function); + duk_get_global_string(self->ctx, function); - if (!duk_is_function(ctx, -1)) { - puts("not callable..."); - duk_pop(ctx); + if (!duk_is_function(self->ctx, -1)) { + duk_pop(self->ctx); return; } for (const char *f = fmt; *f; ++f) { - bool array = false; void (*push)(duk_context *, void *); switch (*f) { - case '>': - array = true; - break; case 'S': - nargs++; - irc_jsapi_server_push(ctx, va_arg(ap, struct irc_server *)); + irc_jsapi_server_push(self->ctx, va_arg(ap, struct irc_server *)); break; case 's': - if (array) { - const char **list = va_arg(ap, const char **), **p; - int i = 0; - - duk_push_array(ctx); - - for (p = list; *p; ++p) { - nargs++; - duk_push_string(ctx, *p); - duk_put_prop_index(ctx, -2, i++); - }; - } else { - nargs++; - duk_push_string(ctx, va_arg(ap, const char *)); - } + duk_push_string(self->ctx, va_arg(ap, const char *)); break; case 'x': - nargs++; push = va_arg(ap, void (*)(duk_context *, void *)); - push(ctx, va_arg(ap, void *)); + push(self->ctx, va_arg(ap, void *)); break; default: - break; + continue; } + + ++nargs; } - if (duk_pcall(ctx, nargs) != 0) { - printf("errro: %s\n", duk_to_string(ctx, -1)); - } + if (duk_pcall(self->ctx, nargs) != 0) + irc_log_warn("plugin %s: %s\n", duk_to_string(self->ctx, -1)); - duk_pop(ctx); + duk_pop(self->ctx); } static void call(struct irc_plugin *plg, const char *function, const char *fmt, ...) { - struct self *self = plg->data; va_list ap; va_start(ap, fmt); - vcall(self->ctx, function, fmt, ap); + vcall(plg, function, fmt, ap); va_end(ap); } @@ -311,12 +302,10 @@ call(plg, "onMode", "Sssssss", ev->server, ev->mode.origin, ev->mode.channel, ev->mode.mode, ev->mode.limit, ev->mode.user, ev->mode.mask); break; -#if 0 case IRC_EVENT_NAMES: - call(plg, "onNames", "Ss>s", ev->names.server, ev->names.channel, - ev->names.names); + call(plg, "onNames", "Ssx", ev->server, ev->names.channel->name, + push_names, ev->names.channel); break; -#endif case IRC_EVENT_NICK: call(plg, "onNick", "Sss", ev->server, ev->nick.origin, ev->nick.nickname); break; diff -r 2ec05b9db2ee -r 95201fd9ad88 lib/irccd/jsapi-server.c --- a/lib/irccd/jsapi-server.c Fri Jan 15 14:44:52 2021 +0100 +++ b/lib/irccd/jsapi-server.c Sat Jan 16 09:45:33 2021 +0100 @@ -22,7 +22,9 @@ #include "channel.h" #include "irccd.h" +#include "jsapi-server.h" #include "server.h" +#include "util.h" #define SIGNATURE DUK_HIDDEN_SYMBOL("Irccd.Server") #define PROTOTYPE DUK_HIDDEN_SYMBOL("Irccd.Server.prototype") @@ -30,15 +32,11 @@ static struct irc_server * self(duk_context *ctx) { - /* - * Server are stored using their identifiers and searched in the - * registry as they may be removed at runtime. - */ struct irc_server *sv; duk_push_this(ctx); duk_get_prop_string(ctx, -1, SIGNATURE); - sv = irc_bot_find_server(duk_to_string(ctx, -1)); + sv = duk_to_pointer(ctx, -1); duk_pop_2(ctx); if (!sv) @@ -56,7 +54,7 @@ duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Server object"); duk_get_prop_string(ctx, index, SIGNATURE); - sv = irc_bot_find_server(duk_to_string(ctx, -1)); + sv = duk_to_pointer(ctx, -1); duk_pop(ctx); return sv; @@ -87,9 +85,28 @@ duk_push_array(ctx); - for (size_t i = 0; i < s->channelsz; ++i) { - duk_push_string(ctx, s->channels[i].name); - duk_put_prop_index(ctx, -2, i); + for (size_t c = 0; c < s->channelsz; ++c) { + duk_push_object(ctx); + duk_push_string(ctx, s->channels[c].name); + duk_put_prop_string(ctx, -2, "name"); + duk_push_boolean(ctx, s->channels[c].joined); + duk_put_prop_string(ctx, -2, "joined"); + duk_push_array(ctx); + + for (size_t n = 0; n < s->channels[c].usersz; ++n) { + duk_push_object(ctx); + duk_push_string(ctx, s->channels[c].users[n].nickname); + duk_put_prop_string(ctx, -2, "nickname"); + if (s->channels[c].users[n].mode) + duk_push_sprintf(ctx, "%c", s->channels[c].users[n].mode); + else + duk_push_null(ctx); + duk_put_prop_string(ctx, -2, "mode"); + duk_put_prop_index(ctx, -2, n); + } + + duk_put_prop_string(ctx, -2, "users"); + duk_put_prop_index(ctx, -2, c); } duk_put_prop_string(ctx, -2, "channels"); @@ -232,9 +249,7 @@ throw server_error(server_error::invalid_channel); #endif -#if 0 - duk_push_boolean(ctx, irc_server_names(channel)); -#endif + duk_push_boolean(ctx, irc_server_names(s, channel)); return 1; } @@ -250,11 +265,9 @@ throw server_error(server_error::invalid_nickname); #endif -#if 0 - duk_push_boolean(set_nickname(std::move(nickname)); -#endif + duk_push_boolean(ctx, irc_server_nick(s, nickname)); - return 0; + return 1; } static duk_ret_t @@ -350,94 +363,176 @@ return 1; } +static inline void +get_name(duk_context *ctx, struct irc_server *s) +{ + duk_get_prop_string(ctx, 0, "name"); + + if (!duk_is_string(ctx, -1)) + duk_error(ctx, DUK_ERR_ERROR, "invalid 'name' property"); + + strlcpy(s->name, duk_to_string(ctx, -1), sizeof (s->name)); + duk_pop(ctx); +} + +static inline void +get_port(duk_context *ctx, struct irc_server *s) +{ + duk_get_prop_string(ctx, 0, "port"); + + if (!duk_is_number(ctx, -1)) + duk_error(ctx, DUK_ERR_ERROR, "invalid 'port' property"); + + s->port = duk_to_int(ctx, -1); + duk_pop(ctx); +} + +static inline void +get_ip(duk_context *ctx, struct irc_server *s) +{ + enum irc_server_flags flags = IRC_SERVER_FLAGS_IPV4 | + IRC_SERVER_FLAGS_IPV6; + + duk_get_prop_string(ctx, 0, "ipv4"); + duk_get_prop_string(ctx, 0, "ipv6"); + + if (duk_is_boolean(ctx, -1) && !duk_to_boolean(ctx, -1)) + flags &= ~(IRC_SERVER_FLAGS_IPV4); + if (duk_is_boolean(ctx, -2) && !duk_to_boolean(ctx, -2)) + flags &= ~(IRC_SERVER_FLAGS_IPV6); + + s->flags |= flags; + duk_pop_n(ctx, 2); +} + +static inline void +get_ssl(duk_context *ctx, struct irc_server *s) +{ + duk_get_prop_string(ctx, 0, "ssl"); + + if (duk_is_boolean(ctx, -1) && duk_to_boolean(ctx, -1)) + s->flags |= IRC_SERVER_FLAGS_SSL; + + duk_pop(ctx); +} + +static inline void +get_string(duk_context *ctx, const char *n, char *dst, size_t dstsz) +{ + duk_get_prop_string(ctx, 0, n); + + if (duk_is_string(ctx, -1) && duk_is_string(ctx ,-1)) + strlcpy(dst, duk_to_string(ctx, -1), dstsz); + + duk_pop(ctx); +} + +static inline void +get_channels(duk_context *ctx, struct irc_server *s) +{ + duk_get_prop_string(ctx, 0, "channels"); + + for (duk_enum(ctx, -1, 0); duk_next(ctx, -1, true); ) { + duk_get_prop_string(ctx, -1, "name"); + duk_get_prop_string(ctx, -2, "password"); + + if (!duk_is_string(ctx, -2)) + duk_error(ctx, DUK_ERR_ERROR, "invalid channel 'name' property"); + + irc_server_join(s, duk_to_string(ctx, -2), duk_opt_string(ctx, -1, NULL)); + duk_pop_n(ctx, 4); + } + + duk_pop_n(ctx, 2); +} + static duk_ret_t Server_constructor(duk_context *ctx) { -#if 0 - return wrap(ctx, [] (auto ctx) { - if (!duk_is_constructor_call(ctx)) - return 0; + struct irc_server s = {0}, *p; - duk_check_type(ctx, 0, DUK_TYPE_OBJECT); + duk_require_object(ctx, 0); - auto json = nlohmann::json::parse(duk_json_encode(ctx, 0)); - auto s = from_json(duk::type_traits::self(ctx).get_service(), json); + get_name(ctx, &s); + get_port(ctx, &s); + get_ip(ctx, &s); + get_ssl(ctx, &s); + get_string(ctx, "nickname", s.nickname, sizeof (s.nickname)); + get_string(ctx, "username", s.username, sizeof (s.username)); + get_string(ctx, "realname", s.realname, sizeof (s.realname)); + get_string(ctx, "commandChar", s.commandchar, sizeof (s.commandchar)); + get_channels(ctx, &s); - duk_push_this(ctx); - duk_push_pointer(ctx, new std::shared_ptr(std::move(s))); - duk_put_prop_string(ctx, -2, signature.data()); - duk_pop(ctx); + p = irc_util_memdup(&s, sizeof (s)); + irc_server_incref(p); - return 0; - }); -#endif + duk_push_this(ctx); + duk_push_pointer(ctx, p); + duk_put_prop_string(ctx, -2, SIGNATURE); + duk_pop(ctx); + return 0; } -#if 0 +static duk_ret_t +Server_destructor(duk_context *ctx) +{ + struct irc_server *sv; + + duk_get_prop_string(ctx, 0, SIGNATURE); + + if ((sv = duk_to_pointer(ctx, -1))) + irc_server_decref(sv); + + duk_pop(ctx); + duk_del_prop_string(ctx, 0, SIGNATURE); + + return 0; +} static duk_ret_t Server_add(duk_context *ctx) { - return wrap(ctx, [] (auto ctx) { - duk::type_traits::self(ctx).get_servers().add( - duk::require>(ctx, 0)); + struct irc_server *sv = require(ctx, 0); - return 0; - }); + return 0; } -#endif - -#if 0 - static duk_ret_t Server_find(duk_context *ctx) { - return wrap(ctx, [] (auto ctx) { - auto id = duk::require(ctx, 0); - auto server = duk::type_traits::self(ctx).get_servers().get(id); - - if (!server) - return 0; + const char *name = duk_require_string(ctx, 0); + struct irc_server *s = irc_bot_find_server(name); - duk::push(ctx, server); + if (!s) + return 0; - return 1; - }); + irc_jsapi_server_push(ctx, s); + + return 1; } -#endif - -#if 0 - static duk_ret_t Server_list(duk_context *ctx) { duk_push_object(ctx); - for (const auto& server : duk::type_traits::self(ctx).get_servers().list()) { - duk::push(ctx, server); - duk_put_prop_string(ctx, -2, server->get_id().c_str()); + for (struct irc_server *s = irc.servers; s; s = s->next) { + irc_jsapi_server_push(ctx, s); + duk_put_prop_string(ctx, -2, s->name); } return 1; } -#endif - -#if 0 - static duk_ret_t Server_remove(duk_context *ctx) { - duk::type_traits::self(ctx).get_servers().remove(duk_require_string(ctx, 0)); + irc_bot_remove_server(duk_require_string(ctx, 0)); return 0; } -#endif - static const duk_function_list_entry methods[] = { { "info", Server_prototype_info, 0 }, { "invite", Server_prototype_invite, 2 }, @@ -459,12 +554,10 @@ }; static const duk_function_list_entry functions[] = { -#if 0 { "add", Server_add, 1 }, { "find", Server_find, 1 }, { "list", Server_list, 0 }, { "remove", Server_remove, 1 }, -#endif { NULL, NULL, 0 } }; @@ -492,6 +585,8 @@ assert(ctx); assert(s); + irc_server_incref(s); + duk_push_object(ctx); duk_push_string(ctx, s->name); duk_put_prop_string(ctx, -2, SIGNATURE); diff -r 2ec05b9db2ee -r 95201fd9ad88 lib/irccd/peer.c --- a/lib/irccd/peer.c Fri Jan 15 14:44:52 2021 +0100 +++ b/lib/irccd/peer.c Sat Jan 16 09:45:33 2021 +0100 @@ -84,6 +84,27 @@ } /* + * DISCONNECT [server] + */ +static int +cmd_server_disconnect(struct irc_peer *p, char *line) +{ + const char *args[1] = {0}; + struct irc_server *s; + + if (parse(line, args, 1) == 1) { + if (!(s = require_server(p, args[0]))) + return 0; + + irc_server_disconnect(s); + } else + for (struct irc_server *s = irc.servers; s; s = s->next) + irc_server_disconnect(s); + + return ok(p); +} + +/* * MESSAGE server channel message */ static int @@ -225,10 +246,10 @@ { char out[IRC_BUF_MAX] = "OK "; - for (size_t i = 0; i < irc.serversz; ++i) { - if (strlcat(out, irc.servers[i].name, sizeof (out)) >= sizeof (out)) + for (struct irc_server *s = irc.servers; s; s = s->next) { + if (strlcat(out, s->name, sizeof (out)) >= sizeof (out)) return EMSGSIZE; - if (i + 1 < irc.serversz && strlcat(out, " ", sizeof (out)) >= sizeof (out)) + if (s->next && strlcat(out, " ", sizeof (out)) >= sizeof (out)) return EMSGSIZE; } diff -r 2ec05b9db2ee -r 95201fd9ad88 lib/irccd/server.c --- a/lib/irccd/server.c Fri Jan 15 14:44:52 2021 +0100 +++ b/lib/irccd/server.c Sat Jan 16 09:45:33 2021 +0100 @@ -96,6 +96,22 @@ return &origin; } +static void +add_nick(const struct irc_server *s, struct irc_channel *ch, const char *nick) +{ + char mode = 0; + + for (size_t i = 0; i < IRC_UTIL_SIZE(s->prefixes); ++i) { + if (nick[0] == s->prefixes[i].token) { + mode = s->prefixes[i].mode; + ++nick; + break; + } + } + + irc_channel_add(ch, nick, mode); +} + static struct irc_channel * add_channel(struct irc_server *s, const char *name, const char *password, bool joined) { @@ -176,25 +192,26 @@ convert_join(struct irc_server *s, struct irc_event *ev) { const struct origin *origin = parse_origin(ev->args[0]); - struct irc_channel *ch; + struct irc_channel *ch = NULL; ev->type = IRC_EVENT_JOIN; ev->server = s; ev->join.origin = ev->args[0]; ev->join.channel = ev->args[2]; - /* Also add a channel if the bot joined. */ - if (strcmp(s->nickname, origin->nickname)) { - if ((ch = irc_server_find(s, ev->args[2]))) - ch->joined = true; - else - add_channel(s, ev->args[2], NULL, true); - } + if (!(ch = irc_server_find(s, ev->args[2]))) + ch = add_channel(s, ev->args[2], NULL, true); + else + ch->joined = true; + + irc_channel_add(ch, origin->nickname, 0); } static void convert_kick(struct irc_server *s, struct irc_event *ev) { + struct irc_channel *ch = irc_server_find(s, ev->args[2]); + ev->type = IRC_EVENT_KICK; ev->server = s; ev->kick.origin = ev->args[0]; @@ -206,16 +223,14 @@ * If the bot was kicked itself mark the channel as not joined and * rejoin it automatically if the option is set. */ - if (strcmp(ev->args[3], s->nickname) == 0) { - struct irc_channel *ch = irc_server_find(s, ev->args[2]); + if (strcmp(ev->args[3], s->nickname) == 0 && ch) { + ch->joined = false; + irc_channel_clear(ch); - if (ch) { - ch->joined = false; - - if (s->flags & IRC_SERVER_FLAGS_AUTO_REJOIN) - irc_server_join(s, ch->name, ch->password); - } - } + if (s->flags & IRC_SERVER_FLAGS_AUTO_REJOIN) + irc_server_join(s, ch->name, ch->password); + } else + irc_channel_remove(ch, ev->args[3]); } static void @@ -305,27 +320,35 @@ static void convert_names(struct irc_server *s, struct irc_event *ev) { - (void)s; - (void)ev; -#if 0 - struct irc_channel *chan; + struct irc_channel *ch; char *p, *n; - if (m->argsz < 3 || !(chan = irc_server_find(s, m->args[2]))) + if (ev->argsz < 6) return; - /* - * Message arguments are as following: - * 0------- 1 2------- 3-------------------- - * yourself = #channel nick1 nick2 nick3 ... - */ - for (p = m->args[3]; p; p = n ? n + 1 : NULL) { - if ((n = strpbrk(p, " "))) - *n = 0; + /* TODO: create if not exist */ + ch = irc_server_find(s, ev->args[4]); + + for (p = ev->args[5]; p; ) { + n = strchr(p, ' '); + + if (n) + *n = '\0'; + if (strlen(p) > 0) + add_nick(s, ch, p); - channel_add(chan, s, p); + p = n ? n + 1 : NULL; } -#endif +} + +static void +convert_endofnames(struct irc_server *s, struct irc_event *ev) +{ + for (size_t i = 0; i < ev->argsz; ++i) + printf("%zd: %s\n", i, ev->args[i]); + ev->type = IRC_EVENT_NAMES; + ev->server = s; + ev->names.channel = irc_server_find(s, ev->args[3]); } static const struct convert { @@ -336,6 +359,7 @@ { "001", convert_connect }, { "005", convert_support }, { "353", convert_names }, + { "366", convert_endofnames }, { "JOIN", convert_join }, { "KICK", convert_kick }, { "MODE", convert_mode }, @@ -976,6 +1000,26 @@ } bool +irc_server_names(struct irc_server *s, const char *channel) +{ + return irc_server_send(s, "NAMES %s", channel); +} + +bool +irc_server_nick(struct irc_server *s, const char *nick) +{ + assert(s); + assert(nick); + + if (s->state == IRC_SERVER_STATE_DISCONNECTED) { + strlcpy(s->nickname, nick, sizeof (s->nickname)); + return true; + } + + return irc_server_send(s, "NICK %s", nick); +} + +bool irc_server_notice(struct irc_server *s, const char *channel, const char *message) { assert(s); @@ -986,11 +1030,22 @@ } void -irc_server_finish(struct irc_server *s) +irc_server_incref(struct irc_server *s) { assert(s); - clear(s); - free(s->channels); - memset(s, 0, sizeof (*s)); + s->refc++; } + +void +irc_server_decref(struct irc_server *s) +{ + assert(s); + assert(s->refc >= 1); + + if (--s->refc == 0) { + clear(s); + free(s->channels); + free(s); + } +} diff -r 2ec05b9db2ee -r 95201fd9ad88 lib/irccd/server.h --- a/lib/irccd/server.h Fri Jan 15 14:44:52 2021 +0100 +++ b/lib/irccd/server.h Sat Jan 16 09:45:33 2021 +0100 @@ -44,7 +44,9 @@ enum irc_server_flags { IRC_SERVER_FLAGS_SSL = (1 << 0), - IRC_SERVER_FLAGS_AUTO_REJOIN = (1 << 1) + IRC_SERVER_FLAGS_AUTO_REJOIN = (1 << 1), + IRC_SERVER_FLAGS_IPV4 = (1 << 2), + IRC_SERVER_FLAGS_IPV6 = (1 << 3) }; struct irc_server_prefix { @@ -97,6 +99,10 @@ enum irc_server_ssl_state ssl_state; #endif + /* Reference count. */ + size_t refc; + struct irc_server *next; + /* IRC server settings. */ char chantypes[8]; struct irc_server_prefix prefixes[16]; @@ -153,9 +159,18 @@ const char *); bool +irc_server_names(struct irc_server *, const char *); + +bool +irc_server_nick(struct irc_server *, const char *); + +bool irc_server_notice(struct irc_server *, const char *, const char *); void -irc_server_finish(struct irc_server *); +irc_server_incref(struct irc_server *); + +void +irc_server_decref(struct irc_server *); #endif /* !IRCCD_SERVER_H */ diff -r 2ec05b9db2ee -r 95201fd9ad88 tests/test-channel.c --- a/tests/test-channel.c Fri Jan 15 14:44:52 2021 +0100 +++ b/tests/test-channel.c Sat Jan 16 09:45:33 2021 +0100 @@ -31,6 +31,11 @@ GREATEST_ASSERT_EQ(ch.users[0].mode, '@'); GREATEST_ASSERT_STR_EQ(ch.users[0].nickname, "markand"); + irc_channel_add(&ch, "markand", '@'); + GREATEST_ASSERT_EQ(ch.usersz, 1U); + GREATEST_ASSERT_EQ(ch.users[0].mode, '@'); + GREATEST_ASSERT_STR_EQ(ch.users[0].nickname, "markand"); + irc_channel_add(&ch, "jean", 0); GREATEST_ASSERT_EQ(ch.usersz, 2U); GREATEST_ASSERT_EQ(ch.users[0].mode, 0);