[mw-devel] MW3 r936 - in trunk: . help src

arthur at sucs.org arthur at sucs.org
Fri Mar 23 09:36:17 GMT 2007


Author: arthur
Date: 2007-03-23 09:36:17 +0000 (Fri, 23 Mar 2007)
New Revision: 936

Added:
   trunk/src/js.c
   trunk/src/js.h
   trunk/src/sqlite.c
   trunk/src/sqlite.h
Modified:
   trunk/help/mwrc
   trunk/mw.spec
   trunk/src/Makefile
   trunk/src/incoming.c
   trunk/src/init.c
   trunk/src/main.c
   trunk/src/script.c
   trunk/src/script.h
Log:
collapse jscript branch back into trunk


Modified: trunk/help/mwrc
===================================================================
--- trunk/help/mwrc	2007-03-22 21:51:55 UTC (rev 935)
+++ trunk/help/mwrc	2007-03-23 09:36:17 UTC (rev 936)
@@ -32,6 +32,7 @@
    easy to split up sections of the .mwrc file, to enable similar parts
    to be grouped together, and they may also be quickly added in or removed
    by uncommenting/commenting the one 'include' directive out.
+   <file> may also be a http/https url, and is a relative path
 
 
 destroy <function | *>

Modified: trunk/mw.spec
===================================================================
--- trunk/mw.spec	2007-03-22 21:51:55 UTC (rev 935)
+++ trunk/mw.spec	2007-03-23 09:36:17 UTC (rev 936)
@@ -1,13 +1,13 @@
 Summary: Milliways III talker and BBS
 Name: mw3
-Version: 2.14.3
-Release: 3
+Version: 2.15.0
+Release: 0
 License: GPL
 Group: Applications/Communications
 Source: %{name}-%{version}.tar.gz
 BuildRoot: %{_tmppath}/%{name}-root
-Requires: libtermcap readline
-BuildRequires: libtermcap-devel readline-devel glibc-devel
+Requires: libtermcap readline sqlite js curl
+BuildRequires: libtermcap-devel readline-devel glibc-devel sqlite-devel js-devel curl-devel
 Packager: Peter Berry <pwb at sucs.org>
 URL: http://sucs.org/wiki/milliways
 

Modified: trunk/src/Makefile
===================================================================
--- trunk/src/Makefile	2007-03-22 21:51:55 UTC (rev 935)
+++ trunk/src/Makefile	2007-03-23 09:36:17 UTC (rev 936)
@@ -7,16 +7,17 @@
 
 # These two only for change after a branch
 VERSION_MAJOR= 2
-VERSION_MINOR= 14
+VERSION_MINOR= 15
 # Change this one on feature increases/fixes, return to 0 on branch
-VERSION_TWEAK= 3
+VERSION_TWEAK= 0
 
 # Full version number
 VERSION=$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_TWEAK)
  
 # cflags for standard 'cc' compiler
 CFLAGS+= -Wall -pedantic -fpie -std=gnu99 -D_GNU_SOURCE
-LDFLAGS+= -lreadline -lhistory -ltermcap -lcrypt -pie
+LDFLAGS+= -pie
+LDLIBS+= -lreadline -lhistory -ltermcap -lcrypt -ljs -lsqlite3 -lcurl
 
 # info strings, do not edit.
 DEFS:= -DBUILD_DATE=\"$(shell date +%Y%m%d)\"
@@ -30,8 +31,8 @@
 #LDFLAGS+= -L. -lefence
 
 ### uncomment for gdb debugging
-#LDFLAGS+= -ggdb -g
-#CFLAGS+= -ggdb -g -D__NO_STRING_INLINE
+LDFLAGS+= -ggdb -g
+CFLAGS+= -ggdb -g -D__NO_STRING_INLINE
 
 ### uncomment for malloc wrappers
 #DEFS+= -DWRAPPERS
@@ -50,9 +51,10 @@
 newmain.c init.c talker.c talker_privs.c colour.c bork.c rooms.c alarm.c\
 wrappers.c topten.c sort.c tidyup.c gags.c script_inst.c script.c\
 incoming.c command.c chattable.c alias.c frl.c hash.c vars.c expand.c\
-mud.c mudtable.c files.c completion.c sentinel.c iconv.c gagtable.c
+mud.c mudtable.c files.c completion.c sentinel.c iconv.c gagtable.c \
+js.c sqlite.c
 
-HDRS = alarm.h dbglue.h gnudb.h Parse.h sort.h alias.h db.h hash.h proto.h special.h bb.h dbutil.h incoming.h rooms.h strings.h bork.h expand.h mesg.h scrcomplete.h talker.h chattable.h files.h mud.h script.h talker_privs.h command.h frl.h mudtable.h script_inst.h wrappers.h completion.h gags.h mwdb.h sentinel.h iconv.h gagtable.c
+HDRS = alarm.h dbglue.h gnudb.h Parse.h sort.h alias.h db.h hash.h proto.h special.h bb.h dbutil.h incoming.h rooms.h strings.h bork.h expand.h mesg.h scrcomplete.h talker.h chattable.h files.h mud.h script.h talker_privs.h command.h frl.h mudtable.h script_inst.h wrappers.h completion.h gags.h mwdb.h sentinel.h iconv.h gagtable.c js.h sqlite.h
 
 OBJS=$(SRCS:.c=.o)
 
@@ -89,7 +91,7 @@
 	cp el_GR.mo locale/el_GR/LC_MESSAGES/mw.mo
 
 bb: $(OBJS)
-	$(CC) $(DEFS) $(CFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS)
+	$(CC) $(DEFS) $(CFLAGS) $(LDFLAGS) -o $(TARGET) $(OBJS) $(LDLIBS)
 	if ! [ -d ../mesgs ] ; then mkdir ../mesgs ; fi
 	if ! [ -f ../who.bb ] ; then touch ../who.bb ; fi
 	if ! [ -f ../log.bb ] ; then touch ../log.bb ; fi
@@ -125,7 +127,7 @@
 	# no longer necessary, rpm does this for you
 	#chmod 4755 $(BB)/mw
 	cd .. && cp -a $(INSTALLFILES) $(BB)
-	cp bbs $(bindir)/mw
+	install -D bbs $(bindir)/mw
 	@echo "Finished Installation."
 
 clean:

Modified: trunk/src/incoming.c
===================================================================
--- trunk/src/incoming.c	2007-03-22 21:51:55 UTC (rev 935)
+++ trunk/src/incoming.c	2007-03-23 09:36:17 UTC (rev 936)
@@ -29,7 +29,7 @@
 extern Alias onoff_list;
 extern Alias ipc_list;
 extern Alias force_list;
-extern int script_output;
+//extern int script_output;
 extern struct person *user;
 extern long userposn;
 extern struct room myroom;

Modified: trunk/src/init.c
===================================================================
--- trunk/src/init.c	2007-03-22 21:51:55 UTC (rev 935)
+++ trunk/src/init.c	2007-03-23 09:36:17 UTC (rev 936)
@@ -13,6 +13,8 @@
 #include <unistd.h>
 #include "bb.h"
 #include "alias.h"
+#include "js.h"
+#include <curl/curl.h>
 
 extern Alias alias_list;
 extern Alias bind_list;
@@ -57,29 +59,63 @@
 	int lineno;
 	struct stat stats;
 
-	if (filename[0] == '/' ||
-	    !strncmp(filename, "../", 3) ||
-	    strstr(filename, "/../"))
-	{
-		printf(_("Cannot load \"%s\": Illegal path\n"), filename);
-		return;
+	if (strncasecmp(filename, "http://", 7)==0
+	||  strncasecmp(filename, "https://", 8)==0) {
+		CURL *cl;
+		char cerr[CURL_ERROR_SIZE];
+		/* use libcurl to grab the file */
+		file = tmpfile();
+		if (file == NULL) {
+			fprintf(stderr, "Error opening temporary file\n");
+		}
+		cl = curl_easy_init();
+/*		curl_easy_setopt(cl, CURLOPT_NOPROGRESS, 0); */
+		curl_easy_setopt(cl, CURLOPT_WRITEDATA, file);
+		curl_easy_setopt(cl, CURLOPT_URL, filename);
+		curl_easy_setopt(cl, CURLOPT_ERRORBUFFER, cerr);
+#ifdef RELEASE
+		if (atoi(VER_TWK) > 0)
+			curl_easy_setopt(cl, CURLOPT_USERAGENT, "Milliways III v" VER_MAJ "." VER_MIN "." VER_TWK);
+		else
+			curl_easy_setopt(cl, CURLOPT_USERAGENT, "Milliways III v" VER_MAJ "." VER_MIN);
+#else
+		curl_easy_setopt(cl, CURLOPT_USERAGENT, "Milliways III v" VER_MAJ "." VER_MIN "." VER_TWK " (Dev)");
+#endif
+		if (curl_easy_perform(cl))
+			fprintf(stderr, "Error loading %s: %s\n", filename, cerr);
+		curl_easy_cleanup(cl);
+		fseek(file, 0, SEEK_SET);
+	} else {
+		if (filename[0] == '/' ||
+		    !strncmp(filename, "../", 3) ||
+		    strstr(filename, "/../"))
+		{
+			printf(_("Cannot load \"%s\": Illegal path\n"), filename);
+			return;
+		}
+		snprintf(path, 1023, "%s/%s", base, filename);
+		if (stat(path, &stats))
+		{
+			if (strcmp(".mwrc", filename))
+				printf(_("Error reading %s: %s\n"), path, strerror(errno));
+			return;
+		}
+		if (!S_ISREG(stats.st_mode))
+		{
+			printf(_("Error reading %s: Not a regular file\n"), path);
+			return;
+		}
+
+		if ((file=fopen(path,"r"))==NULL)
+		{
+			if (strcmp(".mwrc", filename))	printf(_("Error reading %s: %s\n"), path, strerror(errno));
+			return;
+		}
 	}
-	snprintf(path, 1023, "%s/%s", base, filename);
-        if (stat(path, &stats))
-        {
-                if (strcmp(".mwrc", filename))
-                        printf(_("Error reading %s: %s\n"), path, strerror(errno));
-                return;
-        }
-	if (!S_ISREG(stats.st_mode))
-	{
-		printf(_("Error reading %s: Not a regular file\n"), path);
-		return;
-	}
 
-	if ((file=fopen(path,"r"))==NULL)
-	{
-		if (strcmp(".mwrc", filename))	printf(_("Error reading %s: %s\n"), path, strerror(errno));
+	if ((a=strrchr(filename, '.'))!=NULL && strncasecmp(a, ".js", 3)==0) {
+		load_jsfile(file, filename);
+		fclose(file);
 		return;
 	}
 
@@ -175,7 +211,14 @@
 				free(header);
 				continue;
 			}
-			ReadInitFile(base, b);
+			if ((c=strrchr(filename,'/'))!=NULL) {
+				char rpath[1024];
+				*c=0;
+				snprintf(rpath,sizeof(rpath),"%s/%s", filename, b);
+				*c='/';
+				ReadInitFile(base, rpath);
+			}else
+				ReadInitFile(base, b);
 		}else
 		/* check for destroying functions */
 		if (!strcasecmp(a, "destroy"))

Copied: trunk/src/js.c (from rev 935, branches/jscript/src/js.c)
===================================================================
--- trunk/src/js.c	                        (rev 0)
+++ trunk/src/js.c	2007-03-23 09:36:17 UTC (rev 936)
@@ -0,0 +1,699 @@
+/* Add javascript functionality using spidermonkey */
+
+#define XP_UNIX
+
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <jsapi.h>
+#include <iconv.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include "bb.h"
+#include "proto.h"
+#include "sqlite.h"
+#include "chattable.h"
+#include "script.h"
+#include "talker_privs.h"
+
+extern struct person *user;
+extern long userposn;
+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;
+
+JSClass globclass = {
+	"milliways", JSCLASS_HAS_PRIVATE,
+	JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,
+	JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,JS_FinalizeStub,
+    JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSClass js_dbresultclass = {
+	"dbresult", JSCLASS_HAS_PRIVATE,
+	JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+	JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
+	JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSClass js_whoclass = {
+	"who", JSCLASS_HAS_PRIVATE,
+	JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+	JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
+	JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+char *
+utf16tolocal(char * utf16, size_t len) {
+	char * local;
+	char * utf16cpy;
+	char * charset;
+	iconv_t conv;
+	size_t nconv;
+	size_t localbytesleft;
+	size_t utf16bytesleft;
+	char * localcpy;
+
+	/* TODO: charset should be replaced with the charset of the locale */
+	charset = "UTF-8";
+	conv = iconv_open(charset, "UTF16");
+	if (conv == (iconv_t)-1) {
+		fprintf(stderr, "utf16tolocal bombed.\n");
+		return NULL;
+	}
+	
+	localbytesleft = (len) * sizeof(char) * 2; /* Urgh, x2 is kludge.. but how else? */
+	utf16bytesleft = (len) * sizeof(jschar);
+	local = (char *)malloc(localbytesleft);
+
+	if (local == NULL) {
+		fprintf(stderr, "Could not allocate memory for iconv\n");
+		return NULL;
+	}
+	
+	localcpy = local;
+	utf16cpy = utf16;
+
+	while (utf16bytesleft > 0) {
+/*		printf("Before: localbytesleft: %d utf16bytesleft: %d\n",
+			(int)localbytesleft, (int)utf16bytesleft); */
+		nconv = iconv(conv, 
+			&utf16cpy, &utf16bytesleft, 
+			&localcpy, &localbytesleft);
+/*		printf("After: localbytesleft: %d utf16bytesleft: %d\n", 
+			(int)localbytesleft, (int)utf16bytesleft); */
+		if (nconv == (size_t)-1) {
+			fprintf(stderr, "utf16tolocal barfed (%d) ", errno);
+			/* iconv barfed, but why? */
+			if (errno == EILSEQ || errno == EINVAL) {
+				/* invalid input sequence, skip it */
+				fprintf(stderr, "Invalid input sequence\n");
+				utf16++;
+				utf16bytesleft--;
+				errno = 0;
+				continue;
+			} else {
+				/* some other error, recover what we can */
+				*(char *)localcpy = '\0';
+				perror("iconv");
+				errno = 0;
+				break;
+			}
+		}
+	}
+	iconv_close(conv);
+	return local;
+}
+
+/* Function for printing to standard out from javascript (helpful for
+ * debugging and demonstrates how to call C from js)
+ */
+static JSBool
+js_print(JSContext *cx, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval __attribute__((unused)) *rval) {
+	JSString *jsmsg;
+	size_t len;
+	uintN i;
+	
+//	jschar * ucmsg;
+	char * msg;
+	if (argc < 1) {
+		return JS_TRUE;
+	}
+	
+	for (i = 0; i<argc; i++) {
+		if (JSVAL_IS_STRING(argv[i])) {
+			jsmsg = JS_ValueToString(cx,argv[i]);
+			len = JS_GetStringLength(jsmsg);
+			//ucmsg = JS_GetStringChars(jsmsg);
+			//msg = utf16tolocal((char *)ucmsg, len);
+			msg = JS_GetStringBytes(jsmsg);
+			display_message(msg, 0, 1);
+			//printf("%s",msg);
+		} else 
+		if (JSVAL_IS_NULL(argv[i])) {
+			display_message("jsval is NULL",0,1);
+		} else
+		if (JSVAL_IS_OBJECT(argv[i])) {
+			printf("jsval at %p\n", (void *)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, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval __attribute__((unused)) *rval) {
+	JSString *jsmsg;
+	char * msg;
+	if (argc < 1) {
+		return JS_FALSE;
+	}
+	
+	if (JSVAL_IS_STRING(argv[0])) {
+		jsmsg = JS_ValueToString(cx,argv[0]);
+		msg = JS_GetStringBytes(jsmsg);
+		DoCommand(msg, chattable);
+		return JS_TRUE;
+	}
+	return JS_FALSE;
+}
+
+/* say to the talker */
+static JSBool
+js_say(JSContext *cx, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval __attribute__((unused)) *rval) {
+	JSString *jsmsg;
+	char * msg;
+	if (argc < 1) {
+		return JS_FALSE;
+	}
+
+	flood++;
+	if (flood > flood_limit) {
+		printf("FLOOD: This script has flooded the room.\n");
+		return JS_FALSE;
+	}
+	
+	if (JSVAL_IS_STRING(argv[0])) {
+		jsmsg = JS_ValueToString(cx,argv[0]);
+		msg = JS_GetStringBytes(jsmsg);
+		chat_say(msg);
+		return JS_TRUE;
+	}
+	return JS_FALSE;
+}
+
+// Create a javascript array of strings from a struct db_data
+JSObject *
+dbdata_to_jsarray(JSContext *cx, struct db_data *data, int ncols) {
+	JSObject *jsdata;
+	JSString *jsstr;
+	jsval jv;
+	int i;
+
+	if (data == NULL || ncols < 1) return NULL;
+
+	jsdata = JS_NewArrayObject(cx, 0, NULL);
+	JS_AddRoot(cx, jsdata);
+
+	for (i = 0; i < ncols; i++) {
+		printf("dbdata_to_jsarray: data @ %p", (void *)data->field[i]);
+		jsstr = JS_NewStringCopyZ(cx, data->field[i]);
+		JS_AddRoot(cx, jsstr);
+		printf(" -> JSString @ %p\n", (void *)jsstr);
+		jv = STRING_TO_JSVAL(jsstr);
+		JS_SetElement(cx, jsdata, i, &jv);
+	}
+	JS_RemoveRoot(cx, jsdata);
+
+	return jsdata;
+}
+
+// Create a javascript array of arrays (see dbdata_to_jsarray()) from
+// a struct db_result
+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_AddRoot(cx, jsarray);
+	JS_SetArrayLength(cx, jsarray, data->cols);
+
+/*	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, i, &jv);
+
+		node = node->next;
+		i++;
+	}
+	JS_RemoveRoot(cx, jsarray);
+
+	return jsarray;
+}
+
+// Provides a javascript function to query the wholist
+static JSBool
+js_wholist(JSContext *cx, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval *rval) {
+	struct person u;
+	struct who w;
+	int ufile, wfile;
+	JSObject *res;
+	int n=0;
+
+	wfile=openwhofile(O_RDWR);
+	ufile=openuserfile(O_RDONLY);
+	if (wfile<0 || ufile<0) return JS_FALSE;
+
+	res = JS_NewArrayObject(cx, 0, NULL);
+	JS_AddRoot(cx, res);
+
+	while (read(wfile,&w,sizeof(w))) {
+		JSObject *line;
+		JSString *jsstr;
+		jsval jv;
+		int rown=0;
+
+		if (w.posn < 0) continue;
+		lseek(ufile,w.posn,SEEK_SET);
+		read(ufile,&u,sizeof(u));
+
+		/* make a new row and populate it */
+		line = JS_NewArrayObject(cx, 0, NULL);
+		/* user name */
+		jsstr = JS_NewStringCopyZ(cx, u.name);
+		jv = STRING_TO_JSVAL(jsstr);
+		JS_SetElement(cx, line, rown++, &jv);
+		/* room number */
+		jv = INT_TO_JSVAL(u.room);
+		JS_SetElement(cx, line, rown++, &jv);
+		/* idle time */
+		jv = INT_TO_JSVAL(time(0)-u.idletime);
+		JS_SetElement(cx, line, rown++, &jv);
+		/* chat modes */
+		jsstr = JS_NewStringCopyZ(cx, display_cmflags(u.chatmode));
+		jv = STRING_TO_JSVAL(jsstr);
+		JS_SetElement(cx, line, rown++, &jv);
+		/* protection level */
+		jv = INT_TO_JSVAL((u.chatmode & CM_PROTMASK) >> CM_PROTSHIFT);
+		JS_SetElement(cx, line, rown++, &jv);
+		jv = INT_TO_JSVAL((u.chatprivs & CP_PROTMASK) >> CP_PROTSHIFT);
+		JS_SetElement(cx, line, rown++, &jv);
+		/* chat privs */
+		jsstr = JS_NewStringCopyZ(cx, display_cpflags(u.chatprivs & user->chatprivs));
+		jv = STRING_TO_JSVAL(jsstr);
+		JS_SetElement(cx, line, rown++, &jv);
+
+		/* stick line into master array */
+		jv = OBJECT_TO_JSVAL(line);
+		JS_SetElement(cx, res, n++, &jv);
+	}
+
+	JS_RemoveRoot(cx, res);
+	*rval = OBJECT_TO_JSVAL(res);
+	return JS_TRUE;
+}
+
+
+// Provides a javascript function to query an sqlite3 database
+static JSBool
+js_doquery(JSContext *cx, JSObject __attribute__((unused)) *obj, uintN argc, jsval *argv, jsval *rval) {
+	struct db_result *dbres;
+	char *dbname;
+	char *query;
+	JSObject *result; // result object were creating
+	JSObject *resarray;
+	int myid;
+	char path[1024];
+	struct passwd *pw;
+
+	if ((pw=getpwuid(getuid()))==NULL) {
+		fprintf(stderr, "Error getting user information\n");
+		return JS_FALSE;
+	}
+
+	if (strcasecmp(pw->pw_name, "bbs")==0) {
+		printf("bbs user is not allowed db access\n");
+		return JS_FALSE;
+	}
+
+	if (argc != 2) {
+		return JS_FALSE;
+	}
+
+	if (!(JSVAL_IS_STRING(argv[0]) && JSVAL_IS_STRING(argv[1]))) {
+		return JS_FALSE;
+	}
+
+	dbname = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
+	query = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
+
+	if (dbname[0] == '/'
+	||  strncmp(dbname, "../", 3)==0
+	||  strstr(dbname, "/../")) {
+		printf("Illegal path element in dbname '%s'\n", dbname);
+		return JS_FALSE;
+	}
+	snprintf(path, sizeof(path), "%s/%s", pw->pw_dir, dbname);
+
+	myid=geteuid();
+	seteuid(getuid());
+	dbres = db_query(path, query);
+	seteuid(myid);
+
+	if (!dbres) {
+		return JS_FALSE;
+	}
+
+	if (0 == dbres->rows) {
+		// No results found (not an error)
+		return JS_TRUE;
+	}
+
+	resarray = dbresult_to_jsarray(cx, dbres);
+	if (!resarray) {
+		return JS_FALSE;
+	}
+	result = JS_NewObject(cx, &js_dbresultclass, NULL, NULL);
+
+	JS_DefineProperty(cx, result, "numrows", INT_TO_JSVAL(dbres->rows), NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT);
+	JS_DefineProperty(cx, result, "data", OBJECT_TO_JSVAL(resarray), NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE);
+
+	JS_SetPrivate(cx, result, dbres);
+
+	*rval = OBJECT_TO_JSVAL(result);
+	return JS_TRUE;
+}
+
+/* Convert a string from local charset to a string of jschar which
+   JS_NewUCString() needs to create a new JSString with unicode 
+   characters in. An appropriate jschar* is created by casting 
+   UTF-16 data to jschar*, which is why we encode to UTF-16 here. */
+jschar *
+local2jschars(char * local) {
+	char * utf16;
+	char * utf16cpy;
+	char * charset;
+	iconv_t conv;
+	size_t nconv;
+	size_t localbytesleft;
+	size_t utf16bytesleft;
+	char * localcpy;
+
+	/* TODO: charset should be replaced with the charset of the locale */
+	charset = "UTF-8";
+	/* Little endian UTF-16 seems to be the correct encoding. */
+	conv = iconv_open("UTF-16LE", charset);
+	if (conv == (iconv_t)-1) {
+		fprintf(stderr, "local2jschars bombed.\n");
+		return NULL;
+	}
+	
+	localbytesleft = (strlen(local)) * sizeof(char);
+	utf16bytesleft = (strlen(local)) * sizeof(jschar);
+	utf16 = (char *)malloc(utf16bytesleft);
+
+	if (utf16 == NULL) {
+		fprintf(stderr, "Could not allocate memory for iconv\n");
+		return NULL;
+	}
+	
+	localcpy = local;
+	utf16cpy = utf16;
+
+	while (localbytesleft > 0) {
+		/* printf("Before: localbytesleft: %d utf16bytesleft: %d\n",
+			localbytesleft, utf16bytesleft); */
+		nconv = iconv(conv, 
+			&localcpy, &localbytesleft, 
+			&utf16cpy, &utf16bytesleft);
+		/* printf("After: localbytesleft: %d utf16bytesleft: %d\n",
+			localbytesleft, utf16bytesleft); */
+		if (nconv == (size_t)-1) {
+			fprintf(stderr, "local2jschars barfed (%d)\n", errno);
+			/* iconv barfed, but why? */
+			if (errno == EILSEQ || errno == EINVAL) {
+				/* invalid input sequence, skip it */
+				fprintf(stderr, "Invalid input sequence\n");
+				local++;
+				localbytesleft--;
+				errno = 0;
+				continue;
+			} else {
+				/* some other error, recover what we can */
+				fprintf(stderr, "Some other error\n");
+				*(char *)utf16cpy = '\0';
+				perror("iconv");
+				errno = 0;
+				break;
+			}
+		}
+	}
+	iconv_close(conv);
+	return (jschar *)utf16;
+}
+
+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_OBJECT(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");
+}
+
+/* Execute some javascript commands */
+int
+js_exec(char * name, int argc, char **argvc) {
+	int i;
+	jschar * js_string;
+	jsval rval;
+	jsval *argv;
+	JSBool ret;
+
+	js_string = NULL;
+	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;
+		}
+		js_string = (jschar *)JS_malloc(jscx, sizeof(jschar) * strlen(argvc[i]));
+			if (!js_string || js_string == NULL) {
+				fprintf(stderr, "Could not allocate memory for string. Aborting slang_exec(%s)\n", name);
+				return -1;
+			}
+		//	js_string = local2jschars(argvc[i]);
+			if (js_string != NULL) {
+		//		argv[i] = STRING_TO_JSVAL(JS_NewUCString(jscx, js_string, strlen(argvc[i])));
+				argv[i] = STRING_TO_JSVAL(JS_NewString(jscx, argvc[i], strlen(argvc[i])));
+			} else {
+				argv[i] = STRING_TO_JSVAL(JS_NewString(jscx, "(Garbled string)", 16));
+			}
+	}
+	
+	ret = JS_CallFunctionName(jscx, jsroot, name, argc, argv, &rval);
+	if (!ret) {
+		printf("JS function '%s' (args: %d) not found.\n", name, argc);
+	}
+	//show_type("js_exec(rval)", rval);
+	if (JSVAL_IS_BOOLEAN(rval) && JSVAL_TO_BOOLEAN(rval)==0) {
+		script_output=0;
+	}
+	JS_free(jscx, js_string);
+	free(argv);
+	return ret ? 1 : 0;
+}
+
+/* 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;
+		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);
+	
+	if (line[0]) {
+		printf("%s\n%s\n", line, pointer);
+	}
+	free(pointer);
+	free(line);
+}
+
+int load_jsfile(FILE *f, char *filename)
+{
+	char *body;
+	int where, len;
+	JSBool success;
+	JSScript *script = NULL;
+	jsval retval;
+	uintN lineno=0;
+
+	where = ftell(f);
+	fseek(f, 0, SEEK_END);
+	len = ftell(f);
+	fseek(f, where, SEEK_SET);
+
+	printf("Loading %d bytes from %s\n", len, filename);
+
+	body = malloc(len+1);
+	fread(body, 1, len, f);
+	body[len]=0;
+
+	/* Compile the js file specified */
+	script = JS_CompileScript(jscx, jsroot, body, len, filename, lineno);
+	free(body);
+	if (script == NULL) {
+		printf("Failed to compile js script: %s\n", filename);
+		return 0;
+	}
+	
+	/* Execute the compiled script */
+	success = JS_ExecuteScript(jscx, jsroot, script, &retval);
+	if (success == JS_FALSE) {
+		printf("Failed to execute js script: %s\n", filename);
+		return 0;
+	}
+
+	return 1;
+}
+
+/* Load and execute a js file */
+int
+load_js(char *filename) {
+	JSBool success;
+	JSScript *script = NULL;
+	jsval retval;
+	
+	/* Compile the js file specified */
+	script = JS_CompileFile(jscx, jsroot, filename);
+	if (script == NULL) {
+		printf("Failed to compile js script: %s\n", filename);
+		return 0;
+	}
+	
+	/* Execute the compiled script */
+	success = JS_ExecuteScript(jscx, jsroot, script, &retval);
+	if (success == JS_FALSE) {
+		printf("Failed to execute js script: %s\n", filename);
+		return 0;
+	}
+	
+	return 1;
+}
+
+/* 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_OBJECT(jv)) {
+		return 0;
+	}
+	if (JS_ObjectIsFunction(jscx, JSVAL_TO_OBJECT(jv))) {
+		return 1;
+	}
+	return 0;
+}
+
+int setup_js(void)
+{
+	JSBool builtins;
+	struct passwd *pw;
+	int is_local=1;
+
+	if ((pw=getpwuid(getuid()))==NULL) {
+		fprintf(stderr, "Error getting user information\n");
+		return -1;
+	}
+
+	if (strcasecmp(pw->pw_name, "bbs")==0) {
+		is_local=0;
+	}
+
+	/* 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;
+	}
+	/* register an error handler */
+	JS_SetErrorReporter(jscx, js_error_handler);
+
+	/* create the root object */
+	jsroot = JS_NewObject(jscx, &globclass, NULL, NULL);
+
+	/* initiate builtin classes */
+	builtins = JS_InitStandardClasses(jscx, jsroot);
+
+	/* initiate local stuff */
+
+	JS_DefineFunction(jscx, jsroot, "print", js_print, 1, 0);
+	JS_DefineFunction(jscx, jsroot, "exec", js_mwexec, 1, 0);
+	JS_DefineFunction(jscx, jsroot, "say", js_say, 1, 0);
+	JS_DefineFunction(jscx, jsroot, "wholist", js_wholist, 0, 1);
+
+	JS_DefineProperty(jscx, jsroot, "whoami", STRING_TO_JSVAL(JS_NewStringCopyZ(jscx,user->name)), NULL, 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 bind functions to events (bind?)
+	 * - one to load another script (include?)
+	 * - get system date/time
+	 * - input line of text (does anyone use this)
+	 */
+
+	return 0;
+}

Copied: trunk/src/js.h (from rev 935, branches/jscript/src/js.h)
===================================================================
--- trunk/src/js.h	                        (rev 0)
+++ trunk/src/js.h	2007-03-23 09:36:17 UTC (rev 936)
@@ -0,0 +1,6 @@
+/* js.c */
+int js_exec(char *name, int argc, char **argvc);
+int load_jsfile(FILE *f, char *filename);
+int load_js(char *filename);
+int is_js(char *name);
+int setup_js(void);

Modified: trunk/src/main.c
===================================================================
--- trunk/src/main.c	2007-03-22 21:51:55 UTC (rev 935)
+++ trunk/src/main.c	2007-03-23 09:36:17 UTC (rev 936)
@@ -626,6 +626,7 @@
 
 	/* initialise script variables */
 	script_init();
+	setup_js();
 
 	/* clear transient talker flags */
 	oldchat = user->chatmode;

Modified: trunk/src/script.c
===================================================================
--- trunk/src/script.c	2007-03-22 21:51:55 UTC (rev 935)
+++ trunk/src/script.c	2007-03-23 09:36:17 UTC (rev 936)
@@ -25,6 +25,7 @@
 #include "Parse.h"
 #include "frl.h"
 #include "wrappers.h"
+#include "js.h"
 
 extern Alias alias_list;
 extern Alias bind_list;
@@ -64,7 +65,7 @@
 char *event_user=NULL;
 
 /* do we want to output text after an event? */
-int script_output = 1;
+volatile int script_output = 1;
 
 /* logon type */
 int talker_logontype = 0;
@@ -656,9 +657,19 @@
 	var_list_t args;
 	int num;
 	int i;
+	char *fish;
 
 	if ((num=ParseLine(line, bits))<1) return;
 
+	if ((fish = FindLinks(bind_list, bits[0])) == NULL) {
+		printf("Script bind '%s' not found.\n", bits[0]);
+		return;
+	}
+
+	if (is_js(fish)) {
+		js_exec(fish, num, bits);
+		return;
+	}
 	runaway=0;
 	flood=0;
 	script_terminate=0;
@@ -741,6 +752,7 @@
 	char *fish=NULL;
 	int old_rights;
 
+	/* publically named scripts */
 	if (bound)
 	{
 		if ((fish = FindLinks(bind_list, name)) == NULL)
@@ -748,12 +760,15 @@
 			return(2);
 		}
 	}
+	/* otherwise use the name were given */
 	if (fish==NULL) fish=strdup(name);
 
+	/* look for a defined function by that name */
 	script=function_list;
 	while (script!=NULL && strcasecmp(fish, script->name)) script=script->next;
 	if (script==NULL)
 	{
+		/* okay, so its not a mwscript function */
 		free(fish);
 		return(1);
 	}
@@ -822,6 +837,17 @@
 	int retval;
 	var_list_t args;
 
+	if (is_js(script)) {
+		char *argv[4];
+		argv[0]=event;
+		argv[1]=who;
+		argv[2]=text;
+		argv[3]=text+pre;
+		busy++;
+		retval=js_exec(script, 4, argv);
+		busy--;
+		return retval;
+	}
 	runaway=0;
 	if (!script_running) { flood=0;}
 	script_terminate=0;
@@ -863,6 +889,17 @@
 	var_list_t	args;
 	int		retval, index;
 
+	if (is_js(script)) {
+		char **argv;
+		argv = calloc(numargs+2,sizeof(char *));
+		argv[0]=event;
+		argv[1]=who;
+		for (index=0;index<numargs;index++)
+			argv[index+2]=aargs[index];
+		retval = js_exec(script, numargs+2, argv);
+		free(argv);
+		return retval;
+	}
 	runaway=0;
 	if (!script_running) { flood=0;}
 	script_terminate=0;

Modified: trunk/src/script.h
===================================================================
--- trunk/src/script.h	2007-03-22 21:51:55 UTC (rev 935)
+++ trunk/src/script.h	2007-03-23 09:36:17 UTC (rev 936)
@@ -231,7 +231,7 @@
 extern int compare_match;
 extern char *event_body_text;
 extern int script_offset;
-extern int script_output;
+extern volatile int script_output;
 extern char *event_user;
 
 char *eval_arg(char *arg, int argc, char **argv);

Copied: trunk/src/sqlite.c (from rev 935, branches/jscript/src/sqlite.c)
===================================================================
--- trunk/src/sqlite.c	                        (rev 0)
+++ trunk/src/sqlite.c	2007-03-23 09:36:17 UTC (rev 936)
@@ -0,0 +1,171 @@
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include "sqlite.h"
+#include <sqlite3.h>
+
+sqlite3*
+db_open(char *dbname)
+{
+	sqlite3 *db;
+	int error = sqlite3_open(dbname, &db);
+
+	if (error != SQLITE_OK) {
+		printf("Error opening db %s: %s\n", dbname, sqlite3_errmsg(db));
+		return NULL;
+	}
+	return db;
+}
+
+void
+db_close(sqlite3 *db)
+{
+/*	printf("Closing database %p\n", (void *)db); */
+	sqlite3_close(db);
+}
+
+int
+db_callback(void *ptr, int ncols, char **row, char **cols)
+{
+	struct db_result *query = ptr;
+	struct db_data *ndata;
+	int i;
+	unsigned int j;
+
+	if (query == NULL) return 1;
+
+	if (query->colNames == NULL) {
+		/* this is our first time */
+		query->colNames = calloc(ncols+1, sizeof(char *));
+		query->data = NULL;
+		query->rows = 0;
+		query->cols = ncols;
+
+		/* copy the column names */
+		for (i=0;i<ncols;i++) {
+			query->colNames[i] = strdup(cols[i]);
+			for (j=0;j<strlen(query->colNames[i]);j++)
+				if (query->colNames[i][j] == '.') query->colNames[i][j]='_';
+		}
+		query->colNames[ncols] = NULL;
+
+		/* fill in more */
+	}
+	query->rows++;
+
+	/* Copy the data in and attach it */
+	ndata = malloc(sizeof(*ndata));
+	ndata->field = calloc(ncols+1, sizeof(char *));
+	for (i=0;i<ncols;i++) ndata->field[i] = strdup(row[i]);
+	ndata->row = query->rows - 1;
+	ndata->next = query->data;
+	query->data = ndata;
+
+	return 0;
+}
+
+struct db_result*
+db_query(char *dbname, char *query)
+{
+	struct db_result *new;
+	int ret;
+	char *error = NULL;
+	sqlite3 *db;
+
+	if (dbname == NULL) return 0;
+
+	db = db_open(dbname);
+	if (!db) {
+		return NULL;
+	}
+
+	new = malloc(sizeof(*new));
+	new->rows = -1;
+	new->cols = -1;
+	new->colNames = NULL;
+	new->data = NULL;
+
+	ret = sqlite3_exec(db, query, db_callback, new, &error);
+
+	if (ret != SQLITE_OK) {
+		printf("Error %s (%d) on query %s\n", error, ret, query);
+		sqlite3_free(error);
+		db_close(db);
+		return NULL;
+	}
+
+	db_close(db);
+	return new;
+}
+
+/* how many rows in that result */
+int db_numrows(struct db_result *result)
+{
+	if (result == NULL) return 0;
+	return result->rows;
+}
+
+/* fetch a row, return it as a struct */
+void db_getrow(struct db_result *result, int *row)
+{
+	struct db_data *ndata;
+	char **field_names;
+	unsigned char *field_types;
+	void **field_values;
+	int i;
+
+	if (result == NULL) return;
+
+	if (*row < 0 || *row >= result->rows) return;
+
+	ndata = result->data;
+	while (ndata!=NULL && ndata->row != *row) ndata=ndata->next;
+
+	if (ndata == NULL) {
+		printf("Failed to find row %d of result.\n", *row);
+		return;
+	}
+	field_names = calloc(result->cols, sizeof(char *));
+        field_types = calloc(result->cols, sizeof(unsigned char));
+        field_values = calloc(result->cols, sizeof(char *));
+
+	for (i=0;i<result->cols;i++) {
+		field_names[i] = result->colNames[i];
+		/* field_types[i] = SLANG_STRING_TYPE; TODO: remove slangism */
+		field_values[i] = &(ndata->field[i]);
+	}
+	/* SLstruct_create_struct(result->cols, field_names, field_types, field_values); */
+}
+
+void db_free(struct db_result *result)
+{
+	struct db_data *ndata, *nnext;
+	int i;
+
+	if (result == NULL) {
+		return;
+	}
+
+	/* throw away the field names */
+	for (i=0;i<result->cols;i++) {
+		free(result->colNames[i]);
+	}
+	free(result->colNames);
+	/* throw away the returned rows */
+	ndata = result->data;
+	while (ndata != NULL) {
+		for (i=0;i<result->cols;i++)
+			free(ndata->field[i]);
+		free(ndata->field);
+		nnext = ndata->next;
+		free(ndata);
+		ndata = nnext;
+	}
+	/* make sure it cant be used after free */
+	result->data = NULL;
+	result->cols = 0;
+	result->rows = 0;
+	result->colNames = 0;
+	free(result);
+}
+

Copied: trunk/src/sqlite.h (from rev 935, branches/jscript/src/sqlite.h)
===================================================================
--- trunk/src/sqlite.h	                        (rev 0)
+++ trunk/src/sqlite.h	2007-03-23 09:36:17 UTC (rev 936)
@@ -0,0 +1,18 @@
+/* Functions to access and interact with SQLite */
+
+struct db_data {
+	char **field;
+	int row;
+	struct db_data *next;
+};
+
+struct db_result {
+	int rows;
+	int cols;
+	char **colNames;
+	struct db_data *data;
+};
+
+struct db_result *db_query(char *dbname, char *query);
+void db_getrow(struct db_result *result, int *row);
+void db_free(struct db_result *result);





More information about the mw-devel mailing list