Skip to content
Commits on Source (3)
......@@ -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;
}
......@@ -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;
......
#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);
......