Mercurial > irccd
changeset 961:32f93ef20122
irccd: add Irccd.Util API and other things in server
author | David Demelier <markand@malikania.fr> |
---|---|
date | Mon, 25 Jan 2021 22:48:55 +0100 |
parents | 4af3140f234d |
children | 63208f5bb0f6 |
files | irccd/main.c lib/CMakeLists.txt lib/irccd/irccd.c lib/irccd/js-plugin.c lib/irccd/js-plugin.h lib/irccd/jsapi-util.c lib/irccd/jsapi-util.h lib/irccd/server.c lib/irccd/server.h |
diffstat | 9 files changed, 386 insertions(+), 40 deletions(-) [+] |
line wrap: on
line diff
--- a/irccd/main.c Mon Jan 25 11:11:19 2021 +0100 +++ b/irccd/main.c Mon Jan 25 22:48:55 2021 +0100 @@ -41,5 +41,6 @@ s = irc_server_new("mlk", "circ", "circ", "circ", "malikania.fr", 6667); irc_server_join(s, "#test", NULL); irc_bot_server_add(s); + irc_bot_plugin_add(irc_js_plugin_open("/Users/markand/test.js")); irc_bot_run(); }
--- a/lib/CMakeLists.txt Mon Jan 25 11:11:19 2021 +0100 +++ b/lib/CMakeLists.txt Mon Jan 25 22:48:55 2021 +0100 @@ -76,6 +76,8 @@ irccd/jsapi-timer.h irccd/jsapi-unicode.c irccd/jsapi-unicode.h + irccd/jsapi-util.c + irccd/jsapi-util.h ) endif ()
--- a/lib/irccd/irccd.c Mon Jan 25 11:11:19 2021 +0100 +++ b/lib/irccd/irccd.c Mon Jan 25 22:48:55 2021 +0100 @@ -372,7 +372,7 @@ } void -irc_bot_add_plugin(struct irc_plugin *p) +irc_bot_plugin_add(struct irc_plugin *p) { assert(p); @@ -386,7 +386,7 @@ } struct irc_plugin * -irc_bot_find_plugin(const char *name) +irc_bot_plugin_find(const char *name) { struct irc_plugin *p; @@ -398,11 +398,11 @@ } void -irc_bot_remove_plugin(const char *name) +irc_bot_plugin_remove(const char *name) { struct irc_plugin *p; - if (!(p = irc_bot_find_plugin(name))) + if (!(p = irc_bot_plugin_find(name))) return; irc_plugin_unload(p);
--- a/lib/irccd/js-plugin.c Mon Jan 25 11:11:19 2021 +0100 +++ b/lib/irccd/js-plugin.c Mon Jan 25 22:48:55 2021 +0100 @@ -38,6 +38,7 @@ #include "jsapi-system.h" #include "jsapi-timer.h" #include "jsapi-unicode.h" +#include "jsapi-util.h" #include "log.h" #include "plugin.h" #include "server.h" @@ -441,6 +442,7 @@ irc_jsapi_system_load(js.ctx); irc_jsapi_timer_load(js.ctx); irc_jsapi_unicode_load(js.ctx); + irc_jsapi_util_load(js.ctx); if (duk_peval_string(js.ctx, script) != 0) { irc_log_warn("plugin %s: %s", plg->name, duk_to_string(js.ctx, -1)); @@ -491,22 +493,23 @@ memset(self, 0, sizeof (*self)); } -bool -irc_js_plugin_open(struct irc_plugin *plg, const char *path) +struct irc_plugin * +irc_js_plugin_open(const char *path) { - assert(plg); assert(path); char *script = NULL; + struct irc_plugin *plg = irc_util_calloc(1, sizeof (*plg)); if (!(script = eat(path))) { irc_log_warn("plugin: %s", strerror(errno)); - return false; + return NULL; } if (!(init(plg, script))) { free(script); - return false; + free(plg); + return NULL; } plg->set_template = set_template; @@ -527,6 +530,5 @@ /* No longer needed. */ free(script); - /* If error occured, init() has logged. */ - return plg->data != NULL; + return plg; }
--- a/lib/irccd/js-plugin.h Mon Jan 25 11:11:19 2021 +0100 +++ b/lib/irccd/js-plugin.h Mon Jan 25 22:48:55 2021 +0100 @@ -23,7 +23,7 @@ struct irc_plugin; -bool -irc_js_plugin_open(struct irc_plugin *, const char *); +struct irc_plugin * +irc_js_plugin_open(const char *); #endif /* !IRCCD_JS_PLUGIN_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/jsapi-util.c Mon Jan 25 22:48:55 2021 +0100 @@ -0,0 +1,315 @@ +/* + * jsapi-util.c -- Irccd.Util API + * + * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <compat.h> + +#include <string.h> + +#include "jsapi-util.h" +#include "server.h" +#include "subst.h" +#include "util.h" + +struct subspack { + struct irc_subst_keyword *kw; + struct irc_subst subst; +}; + +struct string { + TAILQ_ENTRY(string) link; + char value[]; +}; + +TAILQ_HEAD(stringlist, string); + +/* + * Read parameters for Irccd.Util.format function, the object is defined as + * following: + * + * { + * date: the date object + * flags: the flags (not implemented yet) + * field1: a field to substitute in #{} pattern + * field2: a field to substitute in #{} pattern + * fieldn: ... + * } + */ +static void +subspack_parse(duk_context *ctx, duk_idx_t index, struct subspack *pkg) +{ + memset(pkg, 0, sizeof (*pkg)); + + if (!duk_is_object(ctx, index)) + return; + + duk_enum(ctx, index, 0); + + while (duk_next(ctx, -1, true)) { + if (strcmp(duk_get_string(ctx, -2), "date") == 0) { + pkg->subst.time = duk_get_number(ctx, -1); + continue; + } + + pkg->kw = irc_util_reallocarray(pkg->kw, ++pkg->subst.keywordsz, + sizeof (*pkg->kw)); + pkg->kw[pkg->subst.keywordsz - 1].key = + irc_util_strdup(duk_opt_string(ctx, -2, "")); + pkg->kw[pkg->subst.keywordsz - 1].value = + irc_util_strdup(duk_opt_string(ctx, -1, "")); + + duk_pop_n(ctx, 2); + } + + pkg->subst.flags = IRC_SUBST_DATE | + IRC_SUBST_KEYWORDS | + IRC_SUBST_ENV | + IRC_SUBST_IRC_ATTRS; + pkg->subst.keywords = pkg->kw; +} + +static inline void +subspack_finish(struct subspack *subst) +{ + for (size_t i = 0; i < subst->subst.keywordsz; ++i) { + free((char *)subst->kw[i].key); + free((char *)subst->kw[i].value); + } + + free(subst->kw); +} + +static struct string * +string_new(const char *v) +{ + struct string *s; + const size_t len = strlen(v); + + s = irc_util_malloc(sizeof (*s) + len + 1); + strcpy(s->value, v); + + return s; +} + +static void +stringlist_finish(struct stringlist *list) +{ + struct string *s, *tmp; + + TAILQ_FOREACH_SAFE(s, list, link, tmp) + free(s); +} + +static void +stringlist_concat(struct stringlist *list, const char *value) +{ + struct string *s; + char *str = irc_util_strdup(value), *token, *p = str; + + while ((token = strtok_r(p, " \t\n", &p))) { + /* TODO: trim and check if empty. */ + s = string_new(token); + TAILQ_INSERT_TAIL(list, s, link); + } + + free(str); +} + +static void +split_from_string(duk_context *ctx, struct stringlist *list) +{ + stringlist_concat(list, duk_require_string(ctx, 0)); +} + +static void +split_from_array(duk_context *ctx, struct stringlist *list) +{ + duk_enum(ctx, 0, DUK_ENUM_ARRAY_INDICES_ONLY); + + while (duk_next(ctx, -1, 1)) { + stringlist_concat(list, duk_to_string(ctx, -1)); + duk_pop_2(ctx); + } +} + +static void +split(duk_context *ctx, duk_idx_t index, struct stringlist *list) +{ + duk_require_type_mask(ctx, index, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_STRING); + TAILQ_INIT(list); + + if (duk_is_string(ctx, index)) + split_from_string(ctx, list); + else if (duk_is_array(ctx, index)) + split_from_array(ctx, list); +} + +static int +limit(duk_context *ctx, duk_idx_t index, const char *name, int value) +{ + if (duk_get_top(ctx) < index || !duk_is_number(ctx, index)) + return value; + + value = duk_to_int(ctx, index); + + if (value <= 0) + (void)duk_error(ctx, DUK_ERR_RANGE_ERROR, + "argument %d (%s) must be positive", index, name); + + return value; +} + +static char * +join(duk_context *ctx, size_t maxc, size_t maxl, const struct stringlist *tokens) +{ + FILE *fp; + char *out = NULL; + size_t outsz = 0, linesz = 0, tokensz; + struct string *token; + + if (!(fp = open_memstream(&out, &outsz))) + return false; + + TAILQ_FOREACH(token, tokens, link) { + tokensz = strlen(token->value); + + if (tokensz >= maxc) { + fclose(fp); + duk_push_error_object(ctx, DUK_ERR_RANGE_ERROR, + "token '%s' could not fit in maxc (%zu)", token, maxc); + return NULL; + } + + /* + * If there is something at the beginning of the line, we must + * append a space. + */ + if (linesz > 0) + tokensz++; + + /* + * This token is going past the maximum of the current line so + * we append a newline character and reset the length to start + * a "new" one. + */ + if (linesz + tokensz > maxc) { + if (maxl == 0) { + fclose(fp); + duk_push_error_object(ctx, "lines exceeds maxl (%zu)", maxl); + return NULL; + } + + fputc('\n', fp); + linesz = 0; + maxl -= 1; + } + + linesz += fprintf(fp, "%s%s", linesz > 0 ? " " : "", token->value); + } + + fflush(fp); + fclose(fp); + + return out; +} + +static duk_ret_t +Util_cut(duk_context *ctx) +{ + struct stringlist tokens; + size_t maxc, maxl, i = 0; + char *lines, *line, *p; + + maxc = limit(ctx, 1, "maxc", 72); + maxl = limit(ctx, 2, "maxl", SIZE_MAX); + + /* Construct a list of words from a string or an array of strings. */ + split(ctx, 0, &tokens); + + /* Join as new lines with a limit of maximum columns and lines. */ + if (!(lines = join(ctx, maxc, maxl, &tokens))) { + stringlist_finish(&tokens); + duk_throw(ctx); + } + + duk_push_array(ctx); + + for (p = lines; (line = strtok_r(p, "\n", &p)); ) { + duk_push_string(ctx, line); + duk_put_prop_index(ctx, -2, i++); + } + + stringlist_finish(&tokens); + free(lines); + + return 1; +} + +static duk_ret_t +Util_format(duk_context *ctx) +{ + const char *str = duk_require_string(ctx, 0); + struct subspack pkg; + char buf[1024] = {0}; + + subspack_parse(ctx, 1, &pkg); + irc_subst(buf, sizeof (buf), str, &pkg.subst); + duk_push_string(ctx, buf); + subspack_finish(&pkg); + + return 1; +} + +static duk_ret_t +Util_splituser(duk_context *ctx) +{ + struct irc_server_user user; + + irc_server_split(duk_require_string(ctx, 0), &user); + duk_push_string(ctx, user.nickname); + + return 1; +} + +static duk_ret_t +Util_splithost(duk_context *ctx) +{ + struct irc_server_user user; + + irc_server_split(duk_require_string(ctx, 0), &user); + duk_push_string(ctx, user.host); + + return 1; +} + +static const duk_function_list_entry functions[] = { + { "cut", Util_cut, DUK_VARARGS }, + { "format", Util_format, DUK_VARARGS }, + { "splituser", Util_splituser, 1 }, + { "splithost", Util_splithost, 1 }, + { NULL, NULL, 0 } +}; + +void +irc_jsapi_util_load(duk_context *ctx) +{ + duk_get_global_string(ctx, "Irccd"); + duk_push_object(ctx); + duk_put_function_list(ctx, -1, functions); + duk_put_prop_string(ctx, -2, "Util"); + duk_pop(ctx); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/jsapi-util.h Mon Jan 25 22:48:55 2021 +0100 @@ -0,0 +1,27 @@ +/* + * jsapi-util.h -- Irccd.Util API + * + * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_JSAPI_UTIL_H +#define IRCCD_JSAPI_UTIL_H + +#include <duktape.h> + +void +irc_jsapi_util_load(duk_context *ctx); + +#endif /* !IRCCD_JSAPI_UTIL_H */
--- a/lib/irccd/server.c Mon Jan 25 11:11:19 2021 +0100 +++ b/lib/irccd/server.c Mon Jan 25 22:48:55 2021 +0100 @@ -71,32 +71,6 @@ return strncmp(s->ident.nickname, nick, strlen(s->ident.nickname)) == 0; } -#if 0 - -struct origin { - char nickname[IRC_NICKNAME_LEN]; - char username[IRC_USERNAME_LEN]; - char host[IRC_HOST_LEN]; -}; - -static const struct origin * -parse_origin(const char *prefix) -{ - static struct origin origin; - char fmt[128]; - - memset(&origin, 0, sizeof (origin)); - snprintf(fmt, sizeof (fmt), "%%%zu[^!]!%%%zu[^@]@%%%zus", - sizeof (origin.nickname) - 1, - sizeof (origin.username) - 1, - sizeof (origin.host) - 1); - sscanf(prefix, fmt, origin.nickname, origin.username, origin.host); - - return &origin; -} - -#endif - static void add_nick(const struct irc_server *s, struct irc_channel *ch, const char *nick) { @@ -854,7 +828,7 @@ if (mode) *mode = 0; if (prefix) - *mode = 0; + *prefix = 0; for (size_t i = 0; i < IRC_UTIL_SIZE(s->params.prefixes); ++i) { if (**nick == s->params.prefixes[i].token) { @@ -867,6 +841,22 @@ } void +irc_server_split(const char *prefix, struct irc_server_user *user) +{ + assert(prefix); + assert(user); + + char fmt[128]; + + memset(user, 0, sizeof (*user)); + snprintf(fmt, sizeof (fmt), "%%%zu[^!]!%%%zu[^@]@%%%zus", + sizeof (user->nickname) - 1, + sizeof (user->username) - 1, + sizeof (user->host) - 1); + sscanf(prefix, fmt, user->nickname, user->username, user->host); +} + +void irc_server_incref(struct irc_server *s) { assert(s);
--- a/lib/irccd/server.h Mon Jan 25 11:11:19 2021 +0100 +++ b/lib/irccd/server.h Mon Jan 25 22:48:55 2021 +0100 @@ -49,6 +49,12 @@ IRC_SERVER_FLAGS_IPV6 = (1 << 4) }; +struct irc_server_user { + char nickname[IRC_NICKNAME_LEN]; + char username[IRC_USERNAME_LEN]; + char host[IRC_HOST_LEN]; +}; + struct irc_server_ident { char nickname[IRC_NICKNAME_LEN]; char username[IRC_USERNAME_LEN]; @@ -155,6 +161,9 @@ irc_server_strip(const struct irc_server *, const char **, char *, char *); void +irc_server_split(const char *, struct irc_server_user *); + +void irc_server_incref(struct irc_server *); void