[mw-devel] [Git][arthur/mw][master] Remove js-moz.c
Andrew Price
welshbyte at sucs.org
Sat Jul 29 20:15:34 BST 2017
Andrew Price pushed to branch master at Justin Mitchell / mw
Commits:
52ffb48b by Andrew Price at 2017-07-29T20:14:23+01:00
Remove js-moz.c
- - - - -
1 changed file:
- − src/client/js-moz.c
Changes:
=====================================
src/client/js-moz.c deleted
=====================================
--- a/src/client/js-moz.c
+++ /dev/null
@@ -1,1270 +0,0 @@
-/* Add javascript functionality using spidermonkey */
-
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <readline/readline.h>
-#include <curl/curl.h>
-#include <jansson.h>
-#include "who.h"
-
-/* Ugly but necessary (this is how gjs shuts up the warnings,
- only gcc 4.6 has a nicer (push/pop) way to do it */
-#pragma GCC diagnostic ignored "-Wstrict-prototypes"
-#pragma GCC diagnostic ignored "-Wbad-function-cast"
-#include <jsdbgapi.h>
-#pragma GCC diagnostic warning "-Wbad-function-cast"
-#pragma GCC diagnostic warning "-Wstrict-prototypes"
-
-#include <sqlite.h>
-#include <iconv.h>
-#include "bb.h"
-#include "chattable.h"
-#include "script.h"
-#include "talker_privs.h"
-#include "talker.h"
-#include "alias.h"
-#include "alarm.h"
-#include "log.h"
-#include "main.h"
-#include "init.h"
-#include "js.h"
-#include "util.h"
-#include "user.h"
-#include "sqlite.h"
-
-extern struct user * const user;
-extern unsigned long rights;
-extern int busy;
-extern int current_rights;
-
-/* The master runtime, context, and root object */
-static JSRuntime *jsrt = NULL;
-static JSContext *jscx = NULL;
-static JSObject *jsroot = NULL;
-int js_interrupted=1;
-struct alarm *js_timeout_event=NULL;
-
-JSClass globclass = {
- "milliways", JSCLASS_GLOBAL_FLAGS,
- JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,JS_StrictPropertyStub,
- JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub, NULL,
- JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
-JSClass js_dbresultclass = {
- "dbresult", JSCLASS_HAS_PRIVATE,
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
- JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
-JSClass js_whoclass = {
- "who", JSCLASS_HAS_PRIVATE,
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
- JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
-JSClass js_userrecordclass = {
- "userrecord", JSCLASS_HAS_PRIVATE,
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
- JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
-JSClass js_termsizeclass = {
- "termsize", JSCLASS_HAS_PRIVATE,
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
- JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
-
-/* prints a warning */
-static void js_warning(JSContext *cx, const char *warning)
-{
- JSStackFrame *fp = NULL;
- JSScript *script;
- const char *filename;
- int lineno;
- jsbytecode *pc;
-
- fp = JS_FrameIterator(cx, &fp); // get the current js stack frame
- script = JS_GetFrameScript(cx, fp); // get the current script from the stack frame
- filename = JS_GetScriptFilename(cx, script); // get the file name of the script
- pc = JS_GetFramePC(cx, fp); // get the current pc fro the stack frome
- lineno = JS_PCToLineNumber(cx, script, pc); // get the actual line number from the pc
-
- printf("JS Warning: %s\nFile: %s:%u\n", warning, filename, lineno+1);
-
-}
-
-/* called if a script runs too long */
-static void js_timeout(void *ptr)
-{
- js_interrupted=2;
- JS_TriggerOperationCallback(jsrt);
-}
-
-/* clears the timeout event when a js finishes (or the js uses input) */
-static void js_clear_timeout(void)
-{
- if(js_timeout_event != NULL)
- {
- js_timeout_event->how = NULL;
- js_timeout_event = NULL; // freed by the alarm functions so we just need to unset it here
- }
-}
-
-/* starts a 3 second timer that will interupt js if it is exceeded */
-static void js_start_timeout(void)
-{
- js_clear_timeout();
- alarm_enable();
- js_timeout_event=alarm_after(3, 0, NULL, &js_timeout);
-}
-
-
-/* Function for printing to standard out from javascript (helpful for
- * debugging and demonstrates how to call C from js) - also useful for event functions
- */
-static JSBool js_print(JSContext *cx, unsigned int argc, jsval *vp)
-{
- unsigned int i;
- jsval *argv = JS_ARGV(cx, vp);
- _autofree char *msg = NULL;
- if (argc < 1) {
- return JS_TRUE;
- }
-
- for (i = 0; i<argc; i++) {
- if (JSVAL_IS_STRING(argv[i]) || JSVAL_IS_NUMBER(argv[i])) {
- msg = JS_EncodeString(cx, JS_ValueToString(cx, argv[i]));
- display_message(msg, 0, 1);
- } else
- if (JSVAL_IS_NULL(argv[i])) {
- display_message("jsval is NULL",0,1);
- } else
- if (!JSVAL_IS_PRIMITIVE(argv[i])) {
- printf("jsval at %p\n", (void *)JSVAL_TO_OBJECT(argv[i]));
- if (JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[i]))) {
- display_message("jsval is an (Array)",0,1);
- }
- }
- }
- return JS_TRUE;
-}
-
-/* execute a talker command */
-static JSBool js_mwexec(JSContext *cx, unsigned int argc, jsval *vp)
-{
- jsval *argv = JS_ARGV(cx, vp);
- _autofree char *msg = NULL;
- if (argc < 1) {
- JS_ReportError(cx, "exec() expects an argument.");
- return JS_FALSE;
- }
-
- if (JSVAL_IS_STRING(argv[0])) {
- msg = JS_EncodeString(cx, JS_ValueToString(cx, argv[0]));
- DoCommand(msg, chattable);
- return JS_TRUE;
- }
- JS_ReportError(cx, "exec() expects a string.");
- return JS_FALSE;
-}
-
-
-/* say to the talker */
-static JSBool js_say(JSContext *cx, unsigned int argc, jsval *vp)
-{
- jsval *argv = JS_ARGV(cx, vp);
- _autofree char *msg = NULL;
-
- if (argc < 1) {
- JS_ReportError(cx, "say() expects an argument.");
- return JS_FALSE;
- }
-
- flood++;
- if (flood > flood_limit) {
- JS_ReportError(cx, "FLOOD: This script has flooded the room.");
- return JS_FALSE;
- }
-
- if (JSVAL_IS_STRING(argv[0]) || JSVAL_IS_NUMBER(argv[0])) {
- msg = JS_EncodeString(cx, JS_ValueToString(cx, argv[0]));
- chat_say(msg);
- return JS_TRUE;
- }
- JS_ReportError(cx, "Error: say() expects a string or a number.");
-
- return JS_FALSE;
-}
-
-/* send an rpc/rpb */
-static JSBool js_rpc(JSContext *cx, unsigned int argc, jsval *vp)
-{
- jsval *argv = JS_ARGV(cx, vp);
- _autofree char *msg = NULL;
- _autofree char *rpc_type = NULL;
- char username[NAMESIZE+1]="";
- int broadcast=0;
-
- if (argc < 3) {
- JS_ReportError(cx, "Error: javascript rpc() expects 3 arguments");
- return JS_FALSE;
- }
- if(JSVAL_IS_INT(argv[0])) {
- if(JSVAL_TO_INT(argv[0]) == K_BROADCAST) {
- broadcast=1;
- }
- }
-
- if (JSVAL_IS_STRING(argv[0])) {
- JS_EncodeStringToBuffer(JS_ValueToString(cx, argv[0]), username, NAMESIZE);
- username[NAMESIZE] = '\0' ;
- }
-
- rpc_type = JS_EncodeString(cx, JS_ValueToString(cx, argv[1]));
- msg = JS_EncodeString(cx, JS_ValueToString(cx, argv[2]));
-
- if((!broadcast && (!username[0])) || !rpc_type || !rpc_type[0]) {
- JS_ReportError(cx, "Error: javascript rpc(): invalid arguments - [%s] [%s]", broadcast ? "BROADCAST":username, rpc_type);
- return JS_FALSE;
- }
-
- sendrpc(username, rpc_type, msg, broadcast);
-
- return JS_TRUE;
-}
-
-/* send an ipc/ipb */
-static JSBool js_ipc(JSContext *cx, unsigned int argc, jsval *vp)
-{
- jsval *argv = JS_ARGV(cx, vp);
- _autofree char *msg = NULL;
- _autofree char *username = NULL;
- int broadcast=0;
- if (argc < 2) {
- JS_ReportError(cx, "Error: javascript ipc() expects 2 arguments");
- return JS_FALSE;
- }
- if(JSVAL_IS_INT(argv[0])) {
- if(JSVAL_TO_INT(argv[0]) == K_BROADCAST) {
- broadcast=1;
- }
- }
- if (JSVAL_IS_STRING(argv[0])) {
- username = JS_EncodeString(cx, JS_ValueToString(cx, argv[0]));
- }
-
- msg = JS_EncodeString(cx, JS_ValueToString(cx, argv[1]));
-
- // not broadcast and no username
- if(broadcast==0 && (!username || username[0]=='\0')) {
- JS_ReportError(cx, "Error: javascript ipc(): expects a username or K_BROADCAST");
- return JS_FALSE;
- }
-
- sendipc(username, msg, broadcast);
- return JS_TRUE;
-}
-
-/* ask a user for extra input */
-static JSBool js_input(JSContext *cx, unsigned int argc, jsval *vp)
-{
- jsval *argv = JS_ARGV(cx, vp);
- JSString *the_jsstring, *jsstr;
- size_t ucs2_length, prompt_length, line_length;
- const jschar *ucs2_string;
- int conv_error;
- _autofree char *prompt = NULL;
- _autofree char *line = NULL;
-
- if(argc > 0) {
- if(JSVAL_IS_STRING(argv[0])) {
- // convert prompt to local - this bit of ugliness is still needed in case people aren't using utf8
- the_jsstring = JS_ValueToString(cx, argv[0]);
- ucs2_length=JS_GetStringLength(the_jsstring);
- ucs2_string=JS_GetStringCharsZ(cx, the_jsstring);
- prompt_length=sizeof(char)*((ucs2_length*3)+1); // bit ugly
- prompt=malloc(prompt_length);
- if(prompt!=NULL) {
- conv_error=convert_string_charset((char *)ucs2_string, "UCS-2", ucs2_length*sizeof(jschar), prompt, "LOCAL//TRANSLIT", prompt_length, NULL, NULL, NULL, NULL, NULL);
- if(conv_error<0) {
- fprintf(stderr, "js_input: convert_string_charset failed with error: %d\n", conv_error);
- }
- }
- } else {
- js_warning(cx, "When a parameter is specified to javascript input() command it should be a string to use as the prompt");
- }
- }
- if( prompt == NULL )
- {
- prompt=strdup("? ");
- }
-
- busy++;
- js_clear_timeout();
- if ((line=readline(prompt))==NULL) line=strdup("");
- js_start_timeout();
- busy--;
-
- line_length = sizeof(jschar) * (strlen(line) + 1);
-
- // likewise as this has come straight from a readline we need a convert from local charset
- jschar *u2_string = malloc(line_length);
- if(u2_string!=NULL) {
- conv_error=convert_string_charset(line, "LOCAL", strlen(line),
- (char *)u2_string, "UTF-16//TRANSLIT", line_length,
- NULL, NULL, NULL, NULL, NULL);
- if(conv_error>=0) {
- jsstr = JS_NewUCStringCopyZ(cx, u2_string);
- } else {
- jsstr = JS_NewStringCopyZ(cx, "(garbled string)");
- }
-
- free(u2_string);
- } else {
- jsstr = JS_NewStringCopyZ(cx, "(garbled string)");
- }
- JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(jsstr));
- return JS_TRUE;
-}
-
-
-/* beep */
-static JSBool js_beep(JSContext *cx, unsigned int argc, jsval *vp)
-{
- jsval *argv = JS_ARGV(cx, vp);
- int i, beeps=0;
- if(argc < 1) {
- beeps=1;
- }
- else if(JSVAL_IS_INT(argv[0])) {
- beeps=JSVAL_TO_INT(argv[0]);
- if(beeps < 1 || beeps > 5) {
- beeps=0;
- js_warning(cx, "beep() will only do between 1 and 5 beeps");
- }
- } else {
- js_warning(cx, "beep() command expects an integer");
- }
- for(i=0;i<beeps;i++)
- {
- write(1,"\7",1);
- }
-
- return JS_TRUE;
-}
-
-/* bind something to a javascript function */
-static JSBool js_bind(JSContext *cx, unsigned int argc, jsval *vp)
-{
- jsval *argv = JS_ARGV(cx, vp);
- _autofree char *jbind = NULL;
- _autofree char *function_name = NULL;
- int bind_type=-1;
- int i=1;
- char msg[MAXTEXTLENGTH];
-
- msg[MAXTEXTLENGTH-1]='\0';
-
- if (argc < 2) {
- JS_ReportError(cx, "Error in javascript: bind expects at least 2 arguments");
- return JS_FALSE;
- }
-
- if (JSVAL_IS_STRING(argv[0])) {
- jbind = JS_EncodeString(cx, JS_ValueToString(cx, argv[0]));
- bind_type=K_BIND;
- } else if (JSVAL_IS_INT(argv[0])) {
- bind_type=JSVAL_TO_INT(argv[0]);
- if(bind_type == K_BIND || bind_type == K_BIND_ALIAS || bind_type == K_BIND_RPC) {
- i++;
- jbind = JS_EncodeString(cx, JS_ValueToString(cx, argv[1]));
- }
- } else {
- JS_ReportError(cx, "Error in javascript: bind expects first argument to be a string or a recognised bind id");
- return JS_FALSE;
- }
- if (argc>= i-1 && JSVAL_IS_STRING(argv[i])) {
- function_name = JS_EncodeString(cx, JS_ValueToString(cx, argv[i]));
-
- } else {
- JS_ReportError(cx, "Error in javascript: bind expects final argument to be a string.");
- return JS_FALSE;
- }
-
- switch(bind_type) {
- case K_BIND:
- if(jbind == NULL || jbind[0]=='\0') {
- JS_ReportError(cx, "Empty bind");
- return JS_FALSE;
- }
- if (AddLink(&bind_list, jbind, function_name))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "Bind %s already exists. Redefined", jbind);
- js_warning(cx, msg);
- }
-
- break;
- case K_BIND_ALIAS:
- if(jbind == NULL || jbind[0]=='\0') {
- JS_ReportError(cx, "Empty bind");
- return JS_FALSE;
- }
- if (AddLink(&alias_list, jbind, function_name))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "Alias %s->%s already exists. Redefined", jbind, function_name);
- js_warning(cx, msg);
- }
-
- break;
- case K_BIND_RPC:
- if(jbind == NULL || jbind[0]=='\0') {
- JS_ReportError(cx, "Empty bind");
- return JS_TRUE;
- }
- if (AddLink(&rpc_list, jbind, function_name))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "RPC %s Bind %s already exists. Redefined", jbind, function_name);
- js_warning(cx, msg);
- }
-
- break;
- case K_BIND_EVENT:
- if(AddLink(&event_list, function_name, ""))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "Event bind %s already exists", function_name);
- js_warning(cx, msg);
- }
- break;
- case K_BIND_ONOFF:
- if(AddLink(&onoff_list, function_name, ""))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "CheckOnOff bind %s already exists", function_name);
- js_warning(cx, msg);
- }
- break;
- case K_BIND_IPC:
- if(AddLink(&ipc_list, function_name, ""))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "IPC bind %s already exists", function_name);
- js_warning(cx, msg);
- }
- break;
- case K_BIND_FORCE:
- if(AddLink(&force_list, function_name, ""))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "Force bind %s already exists", function_name);
- js_warning(cx, msg);
- }
- break;
- case K_BIND_SHUTDOWN:
- if(AddLink(&shutdown_list, function_name, ""))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "Shutdown bind %s already exists", function_name);
- js_warning(cx, msg);
- }
-
- break;
- case K_BIND_INPUT:
- if(AddLink(&eventin_list, function_name, ""))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "Input Event bind %s already exists", function_name);
- js_warning(cx, msg);
- }
- break;
- default:
- snprintf(msg, MAXTEXTLENGTH-1, "Unknown bind type %d", bind_type);
- js_warning(cx, msg);
- break;
- }
- return JS_TRUE;
-}
-
-static JSBool js_unbind(JSContext *cx, unsigned int argc, jsval *vp)
-{
- jsval *argv = JS_ARGV(cx, vp);
- _autofree char *jbind = NULL;
- char *function_name=NULL;
- int bind_type=-1;
- char msg[MAXTEXTLENGTH];
-
- msg[MAXTEXTLENGTH-1]='\0';
-
- if (argc < 1) {
- JS_ReportError(cx, "Error in javascript: unbind expects at least 1 argument");
- return JS_FALSE;
- }
-
- if (JSVAL_IS_STRING(argv[0])) {
- jbind = JS_EncodeString(cx, JS_ValueToString(cx, argv[0]));
- bind_type=K_BIND;
- } else if (JSVAL_IS_INT(argv[0])) {
- bind_type=JSVAL_TO_INT(argv[0]);
- if(JSVAL_IS_STRING(argv[1])) {
- jbind = JS_EncodeString(cx, JS_ValueToString(cx, argv[1]));
- function_name = jbind;
- } else {
- JS_ReportError(cx, "Error in javascript: bind expects final argument to be a string.");
- return JS_FALSE;
- }
-
- } else {
- JS_ReportError(cx, "Error in javascript: unbind expects first argument to be a string or a recognised bind id");
- return JS_FALSE;
- }
- switch(bind_type) {
- case K_BIND:
- if(jbind == NULL || jbind[0]=='\0') {
- JS_ReportError(cx, "Error: Empty bind");
- return JS_FALSE;
- }
- if(!DestroyLink(&bind_list, jbind))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "Bind %s does not exist for unbinding", jbind);
- js_warning(cx, msg);
- }
-
- break;
- case K_BIND_ALIAS:
- if(jbind == NULL || jbind[0]=='\0') {
- JS_ReportError(cx, "Empty bind");
- return JS_TRUE;
- }
- if (!DestroyLink(&alias_list, jbind))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "Alias %s does not exist for unbinding", jbind);
- js_warning(cx, msg);
- }
-
- break;
- case K_BIND_RPC:
- if(jbind == NULL || jbind[0]=='\0') {
- JS_ReportError(cx, "Empty bind");
- return JS_TRUE;
- }
- if (!DestroyLink(&rpc_list, jbind))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "RPC %s does not exist for unbinding", jbind);
- js_warning(cx, msg);
- }
-
- break;
- case K_BIND_EVENT:
- if(!DestroyLink(&event_list, function_name))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "Event bind %s does not exist for unbinding", function_name);
- js_warning(cx, msg);
- }
- break;
- case K_BIND_ONOFF:
- if(!DestroyLink(&onoff_list, function_name))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "CheckOnOff bind %s does not exist for unbinding", function_name);
- js_warning(cx, msg);
- }
- break;
- case K_BIND_IPC:
- if(!DestroyLink(&ipc_list, function_name))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "IPC bind %s does not exist for unbinding", function_name);
- js_warning(cx, msg);
- }
- break;
- case K_BIND_FORCE:
- if(!DestroyLink(&force_list, function_name))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "Force bind %s does not exist for unbinding", function_name);
- js_warning(cx, msg);
- }
- break;
- case K_BIND_SHUTDOWN:
- if(!DestroyLink(&shutdown_list, function_name))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "Shutdown bind %s does not exist for unbinding", function_name);
- js_warning(cx, msg);
- }
-
- break;
- case K_BIND_INPUT:
- if(!DestroyLink(&eventin_list, function_name))
- {
- snprintf(msg, MAXTEXTLENGTH-1, "Input Event bind %s does not exist for unbinding", function_name);
- js_warning(cx, msg);
- }
- break;
- default:
- snprintf(msg, MAXTEXTLENGTH-1, "Unknown unbind type %d", bind_type);
- js_warning(cx, msg);
- break;
- }
- return JS_TRUE;
-}
-
-// return the users terminal dimensions
-static JSBool js_termsize(JSContext *cx, unsigned int argc, jsval *vp)
-{
- JSObject *result_object;
- int width, height;
-
- result_object=JS_NewObject(cx, &js_termsizeclass, NULL, NULL);
- if(result_object==NULL)
- {
- JS_ReportError(cx, "An error occured creating an object to hold the term dimensions.");
-
- return JS_FALSE;
- }
-
- width=screen_w();
- height=screen_h();
-
- JS_DefineProperty(cx, result_object, "width", INT_TO_JSVAL(width), NULL, NULL, 0);
- JS_DefineProperty(cx, result_object, "height", INT_TO_JSVAL(height), NULL, NULL, 0);
- JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result_object));
-
- return JS_TRUE;
-}
-
-// Provides a javascript function to query the wholist
-static JSBool js_wholist(JSContext *cx, unsigned int argc, jsval *vp) {
- JSObject *res;
- int n=0;
-
- json_t * wlist = grab_wholist();
- if (wlist == NULL) {
- JS_ReportError(cx, "Could not grab wholist, try again");
- return JS_FALSE;
- }
-
- res = JS_NewArrayObject(cx, 0, NULL);
- JS_AddObjectRoot(cx, &res);
-
- size_t wi;
- json_t *entry;
- time_t now = time(0);
- json_array_foreach(wlist, wi, entry) {
- json_t * perms = json_object_get(entry, "perms");
-
- JSObject *user_record;
- JSString *jsstr;
- jsval jv, user_jv;
-
- /* make a new row and populate it */
- user_record = JS_NewObject(cx, &js_userrecordclass, NULL, NULL);
- user_jv = OBJECT_TO_JSVAL(user_record);
- /* user name */
- jsstr = JS_NewStringCopyZ(cx, json_getstring(entry, "name"));
- jv = STRING_TO_JSVAL(jsstr);
- JS_DefineProperty(cx, user_record, "username", jv, NULL, NULL, 0);
- /* room number */
- jv = INT_TO_JSVAL( json_getint(entry, "channel") );
- JS_DefineProperty(cx, user_record, "room", jv, NULL, NULL, 0);
- /* idle time */
- jv = INT_TO_JSVAL(now - json_getint(entry, "idletime"));
- JS_DefineProperty(cx, user_record, "idle", jv, NULL, NULL, 0);
- /* chat modes */
- jsstr = JS_NewStringCopyZ(cx, json_getstring(entry, "chatmode"));
- jv = STRING_TO_JSVAL(jsstr);
- JS_DefineProperty(cx, user_record, "chatmodes", jv, NULL, NULL, 0);
- /* protection level */
- const char * prot = json_getstring(perms, "protection");
- jv = INT_TO_JSVAL( prot[0] - '0');
- JS_DefineProperty(cx, user_record, "protection_level", jv, NULL, NULL, 0);
- jv = INT_TO_JSVAL( prot[2] - '0');
- JS_DefineProperty(cx, user_record, "protection_power", jv, NULL, NULL, 0);
- /* chat perms */
- jsstr = JS_NewStringCopyZ(cx, json_getstring(perms, "chatprivs") );
- jv = STRING_TO_JSVAL(jsstr);
- JS_DefineProperty(cx, user_record, "chatprivs", jv, NULL, NULL, 0);
- /* status string */
- jsstr = JS_NewStringCopyZ(cx, json_getstring(entry, "doing"));
- jv = STRING_TO_JSVAL(jsstr);
- JS_DefineProperty(cx, user_record, "doing", jv, NULL, NULL, 0);
-
- int dowhen = json_getint(entry, "dowhen");
- if (dowhen)
- jv = INT_TO_JSVAL(now - dowhen);
- else
- jv = INT_TO_JSVAL(0);
- JS_DefineProperty(cx, user_record, "since", jv, NULL, NULL, 0);
-
- /* stick line into master array */
- JS_SetElement(cx, res, n++, &user_jv);
- }
-
- JS_RemoveObjectRoot(cx, &res);
- JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(res));
- json_decref(wlist);
- return JS_TRUE;
-}
-
-/* recieve data from curl into a malloced memory chunk */
-static size_t urldata( void *ptr, size_t size, size_t nmemb, void *stream)
-{
- int addsize = size*nmemb;
- struct block_t *b = stream;
-
- if (stream == NULL) return 0;
- return block_append(b, ptr, addsize);
-}
-
-/* Function to make a url GET request and return the resulting page
- */
-static JSBool js_urlget(JSContext *cx, unsigned int argc, jsval *vp)
-{
- jsval *argv = JS_ARGV(cx, vp);
- _autofree char *url = NULL;
- JSString *jsstr;
- char msg[MAXTEXTLENGTH];
-
- msg[MAXTEXTLENGTH-1]='\0';
-
- if (argc < 1) {
- JS_ReportError(cx, "Bad Call to js_urlget\n");
- return JS_FALSE;
- }
-
- if (JSVAL_IS_STRING(argv[0])) {
- CURL *cl;
- char cerr[CURL_ERROR_SIZE];
- struct block_t *answer = block_new(1024);
-
- url = JS_EncodeString(cx, JS_ValueToString(cx, argv[0]));
- cl = curl_easy_init();
- curl_easy_setopt(cl, CURLOPT_WRITEFUNCTION, urldata);
- curl_easy_setopt(cl, CURLOPT_WRITEDATA, answer);
- curl_easy_setopt(cl, CURLOPT_URL, url);
- curl_easy_setopt(cl, CURLOPT_ERRORBUFFER, cerr);
- curl_easy_setopt(cl, CURLOPT_USERAGENT, "Milliways III v" VERSION);
- if (curl_easy_perform(cl)) {
- snprintf(msg, MAXTEXTLENGTH-1, "JavaScript urlget failed %s: %s", url, cerr);
- js_warning(cx, msg);
- }
- curl_easy_cleanup(cl);
-
- jsstr = JS_NewStringCopyZ(cx, answer->p_buffer);
- JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(jsstr));
-
- free(answer);
- return JS_TRUE;
-
-
- }
- JS_ReportError(cx, "urlget requires a string.");
-
- return JS_FALSE;
-}
-
-// Create a javascript array of strings from a struct db_data
-static JSObject *dbdata_to_jsarray(JSContext *cx, struct db_data *data, int ncols) {
- JSObject *jsdata;
- JSString *jsstr;
- jsval jv;
- int i;
-// int conv_error;
-// size_t data_length;
-
- if (data == NULL || ncols < 1) return NULL;
-
- jsdata = JS_NewArrayObject(cx, 0, NULL);
- JS_AddObjectRoot(cx, &jsdata);
-
- for (i = 0; i < ncols; i++) {
-// printf("dbdata_to_jsarray: data @ %p -", (void *)data->field[i]);
-// printf("%s", data->field[i]);
-// printf(" -> JSString @ %p\n", (void *)jsstr);
- if(data->field[i] != NULL) {
- jsstr = JS_NewStringCopyZ(cx, data->field[i]);
- jv = STRING_TO_JSVAL(jsstr);
- } else {
- jv = JSVAL_NULL;
- }
- JS_SetElement(cx, jsdata, i, &jv);
-
- }
- JS_RemoveObjectRoot(cx, &jsdata);
-
- return jsdata;
-}
-
-// Create a javascript array of arrays (see dbdata_to_jsarray()) from
-// a struct db_result
-static JSObject *dbresult_to_jsarray(JSContext *cx, struct db_result *data) {
- JSObject *jsarray;
- JSObject *jsnode;
- jsval jv;
- struct db_data *node;
- int i;
-
- if (data == NULL) return NULL;
-
- jsarray = JS_NewArrayObject(cx, 0, NULL);
- JS_AddObjectRoot(cx, &jsarray);
-
-/* printf("Making Array(%d)\n", data->cols); */
- i = 0;
- node = data->data;
- while (node) {
-// printf("dbresult_to_jsarray: node @ %p\n", (void *)node);
- jsnode = dbdata_to_jsarray(cx, node, data->cols);
- jv = OBJECT_TO_JSVAL(jsnode);
- JS_SetElement(cx, jsarray, data->rows-1-i, &jv);
-
- node = node->next;
- i++;
- }
- JS_RemoveObjectRoot(cx, &jsarray);
-
- return jsarray;
-}
-
-// create a javascript array of column names from a db_result
-static JSObject *dbresult_to_jscolnames(JSContext *cx, struct db_result *data) {
- JSObject *jsarray;
- jsval jv;
- int i;
- JSString *jsstr;
-
- if (data == NULL) return NULL;
-
- jsarray = JS_NewArrayObject(cx, 0, NULL);
- JS_AddObjectRoot(cx, &jsarray);
-
- for(i=0;i<data->cols;i++) {
- jsstr = JS_NewStringCopyZ(cx, data->colNames[i]);
- jv = STRING_TO_JSVAL(jsstr);
- JS_SetElement(cx, jsarray, i, &jv);
-
- }
-
- JS_RemoveObjectRoot(cx, &jsarray);
-
- return jsarray;
-}
-
-// creates a javascript db object from a struct js_db_result
-// on success it return JS_TRUE and the pointer db_object points to the jsval of the new object
-// on fail returns JS_FALSE
-static JSBool dbresult_to_jsdbobject(JSContext *cx, struct js_db_result *data, jsval *db_object)
-{
- JSObject *result_object;
- JSObject *jsarray, *jscolumns;
- jsval jsarray_val, jscolumns_val;
-
- result_object=JS_NewObject(cx, &js_dbresultclass, NULL, NULL);
- if(result_object==NULL) {
- return JS_FALSE;
- }
- *db_object=OBJECT_TO_JSVAL(result_object);
-
- JS_DefineProperty(cx, result_object, "db_error", INT_TO_JSVAL(data->db_error), NULL, NULL, JSPROP_READONLY);
- JS_DefineProperty(cx, result_object, "query_error", INT_TO_JSVAL(data->query_error), NULL, NULL, JSPROP_READONLY);
- if(data->error_text!=NULL) {
-
- JS_DefineProperty(cx, result_object, "error_text", STRING_TO_JSVAL(JS_NewStringCopyZ(cx, data->error_text)), NULL, NULL, JSPROP_READONLY);
- } else {
- JS_DefineProperty(cx, result_object, "error_text", STRING_TO_JSVAL(JS_NewStringCopyZ(cx, "No Error")), NULL, NULL, JSPROP_READONLY);
- }
- if(data->query_result!=NULL) {
- jsarray=dbresult_to_jsarray(cx, data->query_result);
- jsarray_val=OBJECT_TO_JSVAL(jsarray);
- JS_DefineProperty(cx, result_object, "data", jsarray_val, NULL, NULL, 0);
- jscolumns=dbresult_to_jscolnames(cx, data->query_result);
- jscolumns_val=OBJECT_TO_JSVAL(jscolumns);
- JS_DefineProperty(cx, result_object, "column_names", jscolumns_val, NULL, NULL, 0);
-
- } else {
- JS_DefineProperty(cx, result_object, "data", JSVAL_NULL, NULL, NULL, 0);
- JS_DefineProperty(cx, result_object, "column_names", JSVAL_NULL, NULL, NULL, 0);
- }
- return JS_TRUE;
-}
-
-// Provides a javascript function to query an sqlite3 database
-// This probably wants updating to not return JS_FALSE as that halts js execution
-// far better to return an error code in rsval which the javascript can handle
-static JSBool js_doquery(JSContext *cx, unsigned int argc, jsval *vp)
-{
- jsval *argv = JS_ARGV(cx, vp);
- struct js_db_result *dbres;
- _autofree char *dbname = NULL;
- _autofree char *query = NULL;
- jsval resobject_jsval;
- char path[1024];
- struct passwd *pw;
- JSBool retval;
-
- if ((pw=getpwuid(getuid()))==NULL) {
- JS_ReportError(cx, "Error getting user information");
- return JS_FALSE;
- }
-
- if (getmylogin()==NULL) {
- JS_ReportError(cx, "bbs user is not allowed db access");
- return JS_FALSE;
- }
-
- if (argc != 2) {
- JS_ReportError(cx, "Error: db_query expects two arguments");
- return JS_FALSE;
- }
-
- if (!(JSVAL_IS_STRING(argv[0]) && JSVAL_IS_STRING(argv[1]))) {
- JS_ReportError(cx, "Error: db_query strings");
- return JS_FALSE;
- }
-
- dbname = JS_EncodeString(cx, JS_ValueToString(cx, argv[0]));
- query = JS_EncodeString(cx, JS_ValueToString(cx, argv[1]));
-
- if (!dbname || dbname[0] == '/'
- || strncmp(dbname, "../", 3)==0
- || strstr(dbname, "/../")) {
- JS_ReportError(cx, "Illegal path element in dbname '%s'", dbname);
- return JS_FALSE;
- }
- snprintf(path, sizeof(path), "%s/%s", pw->pw_dir, dbname);
-
- perms_drop();
- dbres = js_db_query(path, query);
- perms_restore();
-
- if (!dbres) {
- JS_ReportError(cx, "Major error in javascript database query.");
- return JS_FALSE;
- }
-
- retval = dbresult_to_jsdbobject(cx, dbres, &resobject_jsval);
- js_db_free(dbres);
-
- JS_SET_RVAL(cx, vp, resobject_jsval);
- return retval;
-}
-
-static JSBool js_store_get(JSContext *cx, JSHandleObject obj, JSHandleId idval, JSMutableHandleValue vp)
-{
- jsval sval;
- JSBool ret = JS_IdToValue(cx, *idval._, &sval);
- if (ret == JS_TRUE && JSVAL_IS_STRING(sval)) {
- _autofree char *key = JS_EncodeString(cx, JS_ValueToString(cx, sval));
- char *val = userdb_get(USERDB_PUBLIC, user->record.name, key);
- if (val == NULL) {
- *vp._ = JSVAL_VOID;
- } else {
- JSString *str = JS_NewStringCopyZ(cx, val);
- free(val);
- *vp._ = STRING_TO_JSVAL(str);
- }
- }
- return JS_TRUE;
-}
-
-static JSBool js_store_set(JSContext *cx, JSHandleObject obj, JSHandleId idval, JSBool strict, JSMutableHandleValue vp)
-{
- jsval sval;
- JSBool ret = JS_IdToValue(cx, *idval._, &sval);
- if (ret == JS_TRUE && JSVAL_IS_STRING(sval) && JSVAL_IS_STRING(*vp._)) {
- _autofree char *key = JS_EncodeString(cx, JS_ValueToString(cx, sval));
- _autofree char *val = JS_EncodeString(cx, JS_ValueToString(cx, *vp._));
- userdb_set(USERDB_PUBLIC, user->record.name, key, val);
- }
- return JS_TRUE;
-}
-
-static JSClass js_storeclass = {
- "Store", JSCLASS_HAS_PRIVATE,
- JS_PropertyStub, JS_PropertyStub, js_store_get, js_store_set,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
- JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
-#if 0
-/* prints the type of a jsval */
-static void show_type(char *name, jsval j)
-{
- printf("%s is:",name);
- if (JSVAL_IS_BOOLEAN(j)) {
- printf(" BOOLEAN");
- printf("=%d",JSVAL_TO_BOOLEAN(j));
- }
- if (JSVAL_IS_DOUBLE(j)) printf(" DOUBLE");
- if (JSVAL_IS_GCTHING(j)) printf(" GCTHING");
- if (JSVAL_IS_INT(j)) {
- printf(" INT");
- printf("=%d", JSVAL_TO_INT(j));
- }
- if (JSVAL_IS_NULL(j)) printf(" NULL");
- if (JSVAL_IS_NUMBER(j)) printf(" NUMBER");
- if (!JSVAL_IS_PRIMITIVE(j)) printf(" OBJECT");
- if (JSVAL_IS_PRIMITIVE(j)) printf(" PRIMITIVE");
- if (JSVAL_IS_STRING(j)) printf(" STRING");
- if (JSVAL_IS_VOID(j)) printf(" VOID");
- printf(" End.\n");
-}
-#endif
-
-/* Execute some javascript commands */
-int js_exec(char *name, int argc, const char **argvc) {
- int i;
- jsval rval;
- jsval *argv;
- JSBool ret;
-
- argv=calloc(argc,sizeof(jsval));
-
- for (i=0;i<argc;i++) {
- if (argvc[i]==NULL) {
- fprintf(stderr, "js_exec: argc %d was NULL!\n",i);
- continue;
- }
- argv[i] = STRING_TO_JSVAL(JS_NewStringCopyZ(jscx, argvc[i]));
- }
- js_interrupted=0;
- js_start_timeout();
- ret = JS_CallFunctionName(jscx, jsroot, name, argc, argv, &rval);
- if (ret) {
- if (JSVAL_IS_BOOLEAN(rval) && JSVAL_TO_BOOLEAN(rval)==0) {
- script_output=0;
- }
- }
- js_clear_timeout();
- //show_type("js_exec(rval)", rval);
- free(argv);
- JS_GC(jsrt); // do we still need to do this now the actual bug has been found?
- return ret;
-}
-
-/* Prints error reports to stdout when a javascript error occurs */
-/* Taken from the spidermonkey tutorial at Kicken's World */
-static void
-js_error_handler(JSContext __attribute__((unused)) *cx, const char *msg, JSErrorReport *er)
-{
- char *pointer=NULL;
- char *line=NULL;
- int len;
-
-
- if (er->linebuf != NULL) {
- len = er->tokenptr - er->linebuf + 1;
- if(len<2) len=2;
- pointer = malloc(len); memset(pointer,
- '-', len); pointer[len-1]='\0';
- pointer[len-2]='^';
- len = strlen(er->linebuf)+1;
- line = malloc(len);
- strncpy(line, er->linebuf, len);
- line[len-1] = '\0';
- } else {
- len = 0;
- pointer = malloc(1);
- line = malloc(1);
- pointer[0]='\0';
- line[0] = '\0';
- }
-
- while (len > 0 && (line[len-1] == '\r' || line[len-1] == '\n')) {
- line[len-1]='\0';
- len--;
- }
-
- printf("JS Error: %s\nFile: %s:%u\n", msg, er->filename, er->lineno+1);
-
- if (line[0]) {
- printf("%s\n%s\n", line, pointer);
- }
- free(pointer);
- free(line);
-}
-
-/* Load and execute a file in javascript */
-/* Files are assumed to be in utf-8 and an error is reported if it isn't */
-/* Non utf-8 chars are stripped */
-int load_jsfile(FILE *f, const char *filename)
-{
- JSBool success;
- JSScript *script;
- jsval retval;
-
- /* Compile the js file specified */
- script = JS_CompileUTF8FileHandle(jscx, jsroot, filename, f);
- if (script == NULL) {
- printf("Failed to compile js script: %s\n", filename);
- return 1;
- }
-
- /* Execute the compiled script */
- js_interrupted=0;
- js_start_timeout();
- success = JS_ExecuteScript(jscx, jsroot, script, &retval);
- if (success == JS_FALSE) {
- printf("Failed to execute js script: %s\n", filename);
- return 1;
- }
- js_clear_timeout();
- return 0;
-}
-
-/* does the named function exist in javascript */
-int is_js(char *name)
-{
- jsval jv;
- JSBool res;
-
- res = JS_GetProperty(jscx, jsroot, name, &jv);
- if (res == JS_FALSE) {
- return 0;
- }
- if (!!JSVAL_IS_PRIMITIVE(jv)) {
- return 0;
- }
- if (JS_ObjectIsFunction(jscx, JSVAL_TO_OBJECT(jv))) {
- return 1;
- }
- return 0;
-}
-
-static JSBool js_operation_callback(JSContext *cx) {
- JSObject *global = JS_GetGlobalObject(cx);
-
- if (global) {
- if (js_interrupted==1) {
- JS_ReportError(cx,"Script execution interupted");
- return JS_FALSE;
- } else if (js_interrupted==2) {
- JS_ReportError(cx,"Script execution time exceeded 3 second limit");
- return JS_FALSE;
- }
-
- }
- return JS_TRUE;
-}
-
-
-
-void js_stop_execution(void)
-{
- js_interrupted=1;
- JS_TriggerOperationCallback(jsrt);
-}
-
-int js_isrunning(void)
-{
- if(jscx) {
- return JS_IsRunning(jscx);
- } else {
- return 0;
- }
-}
-
-// cleans up the javascript environment
-int stop_js(void)
-{
- if (jscx != NULL) JS_DestroyContext(jscx);
- if (jsrt != NULL) JS_DestroyRuntime(jsrt);
- return 0;
-
-}
-
-// starts the javascript engine.
-int setup_js(void)
-{
- int is_local=1;
-
- if (getmylogin()==NULL) {
- is_local=0;
- }
-
- JS_SetCStringsAreUTF8();
- /* create global runtime, allocate memory */
- if (!(jsrt = JS_NewRuntime(8*1024*1024))) {
- printf("Error creating JS runtime\n");
- return -1;
- }
- /* create global js context, allocate stack */
- if (!(jscx = JS_NewContext(jsrt, 8*1024))) {
- printf("Error creating JS Context\n");
- return -1;
- }
-
- JS_BeginRequest(jscx);
- JS_SetOptions(jscx, JSOPTION_VAROBJFIX | JSOPTION_METHODJIT);
- JS_SetVersion(jscx, JSVERSION_LATEST);
- JS_SetErrorReporter(jscx, js_error_handler);
-
- /* create the root object */
- jsroot = JS_NewGlobalObject(jscx, &globclass, NULL);
- if (jsroot == NULL) {
- printf("Failed to create global js object\n");
- JS_EndRequest(jscx);
- return -1;
- }
-
- /* initiate builtin classes */
- JS_InitStandardClasses(jscx, jsroot);
-
- /* create a callback function that is run when javascript branches backwards */
- /* allows us to interupt looping scripts */
-#ifdef JS_OPERATION_WEIGHT_BASE
- /* libmozjs-dev 1.9.0.15 */
- JS_SetOperationCallback(jscx, js_operation_callback, 100 * JS_OPERATION_WEIGHT_BASE);
-#else
- /* libmozjs-dev 1.9.1.5 */
- JS_SetOperationCallback(jscx, js_operation_callback);
-#endif
- /* initiate local stuff */
-
- JS_DefineFunction(jscx, jsroot, "print", js_print, 1, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineFunction(jscx, jsroot, "exec", js_mwexec, 1, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineFunction(jscx, jsroot, "say", js_say, 1, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineFunction(jscx, jsroot, "wholist", js_wholist, 0, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineFunction(jscx, jsroot, "rpc", js_rpc, 3, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineFunction(jscx, jsroot, "ipc", js_ipc, 2, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineFunction(jscx, jsroot, "urlget", js_urlget, 1, JSPROP_READONLY|JSPROP_PERMANENT);
-
- JS_DefineProperty(jscx, jsroot, "whoami", STRING_TO_JSVAL(JS_NewStringCopyZ(jscx,user->record.name)),
- NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
-
- JS_DefineFunction(jscx, jsroot, "beep", js_beep, 1, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineFunction(jscx, jsroot, "input", js_input, 2, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineFunction(jscx, jsroot, "termsize", js_termsize, 0, JSPROP_READONLY|JSPROP_PERMANENT);
-
- JS_DefineFunction(jscx, jsroot, "bind", js_bind, 2, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineFunction(jscx, jsroot, "unbind", js_unbind, 2, JSPROP_READONLY|JSPROP_PERMANENT);
-
- // Set the bind type constants
- JS_DefineProperty(jscx, jsroot, "K_BIND_EVENT", INT_TO_JSVAL(K_BIND_EVENT), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineProperty(jscx, jsroot, "K_BIND_IPC", INT_TO_JSVAL(K_BIND_IPC), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineProperty(jscx, jsroot, "K_BIND_ONOFF", INT_TO_JSVAL(K_BIND_ONOFF), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineProperty(jscx, jsroot, "K_BIND_FORCE", INT_TO_JSVAL(K_BIND_FORCE), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineProperty(jscx, jsroot, "K_BIND_SHUTDOWN", INT_TO_JSVAL(K_BIND_SHUTDOWN), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineProperty(jscx, jsroot, "K_BIND_RPC", INT_TO_JSVAL(K_BIND_RPC), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineProperty(jscx, jsroot, "K_BIND_ALIAS", INT_TO_JSVAL(K_BIND_ALIAS), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
- JS_DefineProperty(jscx, jsroot, "K_BIND_INPUT", INT_TO_JSVAL(K_BIND_INPUT), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
-
- // Broadcast constant (for ipc,rpc)
- JS_DefineProperty(jscx, jsroot, "K_BROADCAST", INT_TO_JSVAL(K_BROADCAST), NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
-
- /* A stateful object backed with persistent db storage */
- JS_DefineObject(jscx, jsroot, "Store", &js_storeclass, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
-
- /* not for bbs user */
- if (is_local) {
- JS_DefineFunction(jscx, jsroot, "dbquery", js_doquery, 2, 1);
- }
-
- /* need additional functions :
- * - one to load another script (include?) - possibly although most scripts are loaded from the .mwrc or using .load
- * - get system date/time - no, 'new Date()' will get this information for you in a Date object.
- */
- JS_EndRequest(jscx);
-
- return 0;
-}
View it on GitLab: https://projects.sucs.org/arthur/mw/commit/52ffb48b380225c2ab23d73cada72dd693d6e183
---
View it on GitLab: https://projects.sucs.org/arthur/mw/commit/52ffb48b380225c2ab23d73cada72dd693d6e183
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/20170729/04e7a832/attachment-0001.html>
More information about the mw-devel
mailing list