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