Skip to content
GitLab
Explore
Sign in
Commits on Source (3)
duktape: Implement input()
· 39f0879f
Andrew Price
authored
Jul 14, 2017
39f0879f
duktape: Implement dbquery()
· 0a588346
Andrew Price
authored
Jul 14, 2017
0a588346
duktape: Implement the Store object
· 35949d1a
Andrew Price
authored
Jul 19, 2017
35949d1a
Hide whitespace changes
Inline
Side-by-side
src/client/js-duk.c
View file @
35949d1a
...
...
@@ -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
View file @
35949d1a
...
...
@@ -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
View file @
35949d1a
#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
);
...
...