[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