[mw-devel] [Git][arthur/mw][master] 3 commits: duktape: Implement input()

Andrew Price welshbyte at sucs.org
Wed Jul 19 19:24:37 BST 2017


Andrew Price pushed to branch master at Justin Mitchell / mw


Commits:
39f0879f by Andrew Price at 2017-07-14T13:27:54+01:00
duktape: Implement input()

- - - - -
0a588346 by Andrew Price at 2017-07-14T16:35:45+01:00
duktape: Implement dbquery()

- - - - -
35949d1a by Andrew Price at 2017-07-19T19:23:47+01:00
duktape: Implement the Store object

- - - - -


3 changed files:

- src/client/js-duk.c
- src/client/sqlite.c
- src/client/sqlite.h


Changes:

=====================================
src/client/js-duk.c
=====================================
--- a/src/client/js-duk.c
+++ b/src/client/js-duk.c
@@ -2,11 +2,14 @@
 #include <sys/stat.h>
 #include <unistd.h>
 #include <stdio.h>
+#include <pwd.h>
 #include <errno.h>
 #include <jansson.h>
 #include <curl/curl.h>
+#include <readline/readline.h>
 #include <duktape.h>
 
+#include <sqlite.h>
 #include "js.h"
 #include "main.h"
 #include "script.h"
@@ -16,6 +19,10 @@
 #include "user.h"
 #include "alarm.h"
 #include "who.h"
+#include "util.h"
+#include "iconv.h"
+#include "sqlite.h"
+#include "init.h"
 
 extern struct user * const user;
 struct alarm *timeout_event = NULL;
@@ -295,6 +302,80 @@ static duk_ret_t js_beep(duk_context *cx)
 	return 0;
 }
 
+/* Caller must free the returned memory */
+static char *prompt_to_local(duk_context *cx)
+{
+	_autofree char *instr = NULL;
+	const char *dukstr;
+	duk_size_t dukbufsize;
+	size_t promptbufsize;
+	char *prompt;
+	size_t len;
+	int err;
+
+	len = duk_get_length(cx, -1);
+	dukstr = duk_get_lstring(cx, -1, &dukbufsize);
+	promptbufsize = sizeof(char) * ((len * 3) + 1); // Still ugly
+	prompt = malloc(promptbufsize); // Freed by caller
+	duk_pop(cx);
+
+	if (dukstr == NULL)
+		return strdup("? ");
+
+	/* To retain const correctness we have to dup the js string
+	   which might seem slow but prompts will usually be short
+	   so it's not worth worrying about */
+	instr = strdup(dukstr);
+	err = convert_string_charset(instr, "UTF-8", dukbufsize,
+	                             prompt, "LOCAL//TRANSLIT", promptbufsize,
+	                             NULL, NULL, NULL, NULL, NULL);
+	if (err < 0) {
+		fprintf(stderr, "mwsjs error: input() failed to convert "
+				"prompt string: %d\n", err);
+	}
+	if (prompt == NULL)
+		return strdup("? ");
+	return prompt;
+}
+
+static duk_ret_t js_input(duk_context *cx)
+{
+	_autofree char *prompt = NULL;
+	_autofree char *outstr = NULL;
+	_autofree char *line = NULL;
+	size_t len;
+	int err;
+
+	if (!duk_is_undefined(cx, -1) && !duk_is_string(cx, -1)) {
+		fprintf(stderr, "mwjs error: argument to input() must be a string\n");
+		return DUK_RET_SYNTAX_ERROR;
+	}
+	prompt = prompt_to_local(cx);
+
+	busy++;
+	clear_timeout();
+	line = readline(prompt);
+	start_timeout();
+	busy--;
+
+	if (line == NULL) {
+		duk_push_string(cx, "");
+		return 1;
+	}
+	len = strlen(line) * 3 + 3; // Also ugly
+	outstr = malloc(len);
+	err = convert_string_charset(
+	                line, "LOCAL", strlen(line),
+                        outstr, "UTF-8//TRANSLIT", len,
+                        NULL, NULL, NULL, NULL, NULL);
+	if (err < 0) {
+		fprintf(stderr, "mwjs error: input(): garbled string\n");
+		return DUK_RET_ERROR;
+	}
+	duk_push_string(cx, outstr);
+	return 1;
+}
+
 static duk_ret_t js_termsize(duk_context *cx)
 {
 	int idx;
@@ -416,6 +497,119 @@ static duk_ret_t js_unbind(duk_context *cx)
 	return 0;
 }
 
+static void js_push_result_entry(duk_context *cx, struct db_data *entry, int ncols)
+{
+	duk_idx_t idx;
+	int i;
+
+	if (entry == NULL || ncols < 1) {
+		duk_push_null(cx);
+		return;
+	}
+	idx = duk_push_array(cx);
+	for (i = 0; i < ncols; i++) {
+		duk_push_string(cx, entry->field[i]);
+		duk_put_prop_index(cx, idx, i);
+	}
+}
+
+static void js_push_result_array(duk_context *cx, struct db_result *dbres)
+{
+	struct db_data *entry;
+	duk_idx_t idx;
+	int i;
+
+	if (dbres == NULL) {
+		duk_push_null(cx);
+		return;
+	}
+	idx = duk_push_array(cx);
+	for (i = 0, entry = dbres->data; entry != NULL; entry = entry->next, i++) {
+		js_push_result_entry(cx, entry, dbres->cols);
+		duk_put_prop_index(cx, idx, i);
+	}
+}
+
+static void js_push_column_names(duk_context *cx, struct db_result *dbres)
+{
+	duk_idx_t idx;
+	int i;
+
+	if (dbres == NULL) {
+		duk_push_null(cx);
+		return;
+	}
+	idx = duk_push_array(cx);
+	for (i = 0; i < dbres->cols; i++) {
+		duk_push_string(cx, dbres->colNames[i]);
+		duk_put_prop_index(cx, idx, i);
+	}
+}
+
+static void js_push_dbresult(duk_context *cx, struct js_db_result *dbres)
+{
+	duk_idx_t idx = duk_push_bare_object(cx);
+	duk_push_int(cx, dbres->db_error);
+	duk_put_prop_string(cx, idx, "db_error");
+	duk_push_int(cx, dbres->query_error);
+	duk_put_prop_string(cx, idx, "query_error");
+	if (dbres->error_text != NULL)
+		duk_push_string(cx, dbres->error_text);
+	else
+		duk_push_string(cx, "No Error");
+	duk_put_prop_string(cx, idx, "error_text");
+	js_push_result_array(cx, dbres->query_result);
+	duk_put_prop_string(cx, idx, "data");
+	js_push_column_names(cx, dbres->query_result);
+	duk_put_prop_string(cx, idx, "column_names");
+}
+
+static duk_ret_t js_dbquery(duk_context *cx)
+{
+	_autofree char *path = NULL;
+	struct js_db_result *dbres;
+	const char *dbname;
+	const char *query;
+	struct passwd *pw;
+
+	if ((pw = getpwuid(getuid())) == NULL) {
+		fprintf(stderr, "mwjs error: dbquery(): Error getting user information\n");
+		return DUK_RET_ERROR;
+	}
+	if (getmylogin() == NULL) {
+		fprintf(stderr, "mwjs error: dbquery(): Permission denied\n");
+		return DUK_RET_ERROR;
+	}
+	if (duk_is_undefined(cx, -1) ||
+	    duk_is_undefined(cx, -2)) {
+		fprintf(stderr, "mwjs error: dbquery() requires 2 arguments\n");
+		return DUK_RET_SYNTAX_ERROR;
+	}
+	if (!(duk_is_string(cx, -1) && duk_is_string(cx, -2))) {
+		fprintf(stderr, "mwjs error: dbquery() requires 2 strings\n");
+		return DUK_RET_ERROR;
+	}
+	dbname = duk_get_string(cx, -2);
+	query = duk_get_string(cx, -1);
+	if (!dbname || dbname[0] == '/' || !strncmp(dbname, "../", 3) ||
+	    strstr(dbname, "/../")) {
+		fprintf(stderr, "mwjs error: dbquery(): illegal path '%s'\n", dbname);
+		return DUK_RET_ERROR;
+	}
+	asprintf(&path, "%s/%s", pw->pw_dir, dbname);
+	perms_drop();
+	dbres = js_db_query(path, query);
+	perms_restore();
+	if (!dbres) {
+		fprintf(stderr, "mwjs error: dbquery(): query failed\n");
+		return DUK_RET_ERROR;
+        }
+	js_push_dbresult(cx, dbres);
+	js_db_free(dbres);
+	return 1;
+}
+
+
 int js_isrunning(void)
 {
 	return 0;
@@ -517,6 +711,46 @@ int stop_js(void)
 	return 0;
 }
 
+static duk_ret_t js_store_get(duk_context *cx)
+{
+	const char *key = duk_get_string(cx, 1);
+	char *val = userdb_get(USERDB_PUBLIC, user->record.name, key);
+	if (val == NULL)
+		duk_push_undefined(cx);
+	else
+		duk_push_string(cx, val);
+	return 1;
+}
+
+static duk_ret_t js_store_set(duk_context *cx)
+{
+	const char *key = duk_get_string(cx, 1);
+	const char *val = duk_get_string(cx, 2);
+	userdb_set(USERDB_PUBLIC, user->record.name, key, val);
+	return 0;
+}
+
+static const duk_function_list_entry store_handlers[] = {
+	{ "get", js_store_get, 3 },
+	{ "set", js_store_set, 4 },
+	{ NULL, NULL, 0 }
+};
+
+/* The owning object must be at the top of the stack when calling this */
+static void define_store_object(void)
+{
+	duk_push_string(ctx, "Store"); /* To be used by duk_def_prop() below */
+
+	duk_get_prop_string(ctx, -2, "Proxy"); /* Push the built-in Proxy constructor */
+	duk_push_object(ctx); /* Store */
+	duk_push_object(ctx); /* Handler object */
+	duk_put_function_list(ctx, -1, store_handlers);
+	duk_new(ctx, 2); /* js equiv: new Proxy(Store, handler); */
+
+	/* Define the Store object in the global object */
+	duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE);
+}
+
 /* The owning object must be at the top of the stack when calling this */
 static void define_func(const char *name, duk_c_function func, int nargs)
 {
@@ -558,6 +792,11 @@ static void define_constants(void)
 
 int setup_js(void)
 {
+	int is_local = 1;
+
+	if (getmylogin() == NULL)
+		is_local = 0;
+
 	ctx = duk_create_heap_default();
 	if (ctx == NULL)
 		return -1;
@@ -572,9 +811,13 @@ int setup_js(void)
 	define_func("ipc", js_ipc, 2);
 	define_func("urlget", js_urlget, 1);
 	define_func("beep", js_beep, 1);
+	define_func("input", js_input, 1);
 	define_func("termsize", js_termsize, 0);
 	define_func("bind", js_bind, 3);
 	define_func("unbind", js_unbind, 2);
+	define_store_object();
+	if (is_local) // Don't allow bbs user to use dbquery
+		define_func("dbquery", js_dbquery, 2);
 	duk_pop(ctx);
 	return 0;
 }


=====================================
src/client/sqlite.c
=====================================
--- a/src/client/sqlite.c
+++ b/src/client/sqlite.c
@@ -68,7 +68,7 @@ static int db_callback(void *ptr, int ncols, char **row, char **cols)
 	return 0;
 }
 
-struct js_db_result* js_db_query(char *dbname, char *query)
+struct js_db_result* js_db_query(char *dbname, const char *query)
 {
 	struct js_db_result *new;
 


=====================================
src/client/sqlite.h
=====================================
--- a/src/client/sqlite.h
+++ b/src/client/sqlite.h
@@ -1,7 +1,7 @@
 #ifndef CLIENT_SQLITE_H
 #define CLIENT_SQLITE_H
 
-extern struct js_db_result* js_db_query(char *dbname, char *query);
+extern struct js_db_result* js_db_query(char *dbname, const char *query);
 extern void js_db_free(struct js_db_result *result);
 extern char * userdb_get(const char *table, const char *user, const char *opt);
 extern void userdb_set(const char *table, const char *user, const char *opt, const char *arg);



View it on GitLab: https://projects.sucs.org/arthur/mw/compare/bea3734d234f4b88e2df77668b7cec5edea3b33e...35949d1ad6a918ec419253798cdd4f6be09b40d0

---
View it on GitLab: https://projects.sucs.org/arthur/mw/compare/bea3734d234f4b88e2df77668b7cec5edea3b33e...35949d1ad6a918ec419253798cdd4f6be09b40d0
You're receiving this email because of your account on projects.sucs.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.sucs.org/pipermail/mw-devel/attachments/20170719/1451de5c/attachment-0001.html>


More information about the mw-devel mailing list