[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