Created
August 23, 2009 20:42
-
-
Save paraboul/173445 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Copyright (C) 2006, 2007, 2008, 2009 Anthony Catel <[email protected]> | |
This file is part of APE Server. | |
APE is free software; you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation; either version 2 of the License, or | |
(at your option) any later version. | |
APE is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with APE ; if not, write to the Free Software Foundation, | |
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
*/ | |
/* Javascript plugins support using (awesome) spidermonkey API */ | |
#define XP_UNIX | |
#define JS_THREADSAFE | |
#include "./js/src/jsapi.h" | |
#include "./js/src/jsobj.h" | |
#include <stdio.h> | |
#include <glob.h> | |
#include "plugins.h" | |
#include "global_plugins.h" | |
#define MODULE_NAME "spidermonkey" | |
/* Return the global SpiderMonkey Runtime instance e.g. ASMR->runtime */ | |
#define ASMR ((ape_sm_runtime *)get_property(g_ape->properties, "sm_runtime")->val) | |
#define APE_JS_EVENT(cb) ape_fire_callback(cb, g_ape) | |
/* JSNative macro prototype */ | |
#define APE_JS_NATIVE(func_name) \ | |
static JSBool func_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) \ | |
{ \ | |
ape_sm_compiled *asc; \ | |
acetables *g_ape; \ | |
asc = JS_GetPrivate(cx, obj); \ | |
g_ape = asc->g_ape; | |
#define APE_JS_NATIVE_WP(func_name) \ | |
static JSBool func_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) \ | |
{ \ | |
ape_sm_compiled *asc; \ | |
acetables *g_ape; \ | |
asc = JS_GetPrivate(cx, JS_GetPrototype(cx, obj)); \ | |
g_ape = asc->g_ape; | |
typedef struct _ape_sm_callback ape_sm_callback; | |
struct _ape_sm_callback | |
{ | |
char *callbackname; | |
jsval func; | |
struct _ape_sm_callback *next; | |
}; | |
typedef struct _ape_sm_compiled ape_sm_compiled; | |
struct _ape_sm_compiled { | |
char *filename; | |
JSScript *bytecode; | |
JSContext *cx; | |
JSObject *global; | |
JSObject *scriptObj; | |
acetables *g_ape; | |
ape_sm_callback *callbacks; | |
struct _ape_sm_compiled *next; | |
}; | |
typedef struct _ape_sm_runtime ape_sm_runtime; | |
struct _ape_sm_runtime { | |
JSRuntime *runtime; | |
ape_sm_compiled *scripts; | |
}; | |
struct _ape_sock_callbacks { | |
jsval on_accept; | |
jsval on_read; | |
jsval on_read_lf; | |
jsval on_disconnect; | |
ape_sm_compiled *asc; | |
void *private; | |
}; | |
static JSBool sockserver_addproperty(JSContext *cx, JSObject *obj, jsval idval, jsval *vp); | |
static ace_plugin_infos infos_module = { | |
"Javascript embeded", // Module Name | |
"0.01", // Module Version | |
"Anthony Catel", // Module Author | |
NULL // Config file | |
}; | |
static JSClass apesocket_class = { | |
"apesocket", JSCLASS_HAS_PRIVATE, | |
sockserver_addproperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, | |
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, | |
JSCLASS_NO_OPTIONAL_MEMBERS | |
}; | |
/* Standard javascript object */ | |
static JSClass global_class = { | |
"global", JSCLASS_GLOBAL_FLAGS, | |
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, | |
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, | |
JSCLASS_NO_OPTIONAL_MEMBERS | |
}; | |
/* The main Ape Object (global) */ | |
static JSClass ape_class = { | |
"Ape", JSCLASS_HAS_PRIVATE, | |
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, | |
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, | |
JSCLASS_NO_OPTIONAL_MEMBERS | |
}; | |
static JSClass socketserver_class = { | |
"sockServer", JSCLASS_HAS_PRIVATE, | |
sockserver_addproperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, | |
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, | |
JSCLASS_NO_OPTIONAL_MEMBERS | |
}; | |
static JSBool apesocket_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) | |
{ | |
ape_socket *client; | |
char *string; | |
//JS_SetContextThread(cx); | |
//JS_BeginRequest(cx); | |
struct _ape_sock_callbacks *cb = JS_GetPrivate(cx, obj); | |
ape_sm_compiled *asc = cb->asc; | |
acetables *g_ape = asc->g_ape; | |
client = cb->private; | |
if (!JS_ConvertArguments(cx, argc, argv, "s", &string)) { | |
return JS_FALSE; | |
} | |
//JS_EndRequest(cx); | |
//JS_ClearContextThread(cx); | |
sendbin(client->fd, string, strlen(string), g_ape); | |
return JS_TRUE; | |
} | |
static JSFunctionSpec apesocket_funcs[] = { | |
JS_FS("write", apesocket_write, 1, 0, 0), | |
JS_FS_END | |
}; | |
static void sm_sock_onaccept(ape_socket *client, acetables *g_ape) | |
{ | |
jsval rval; | |
if (client->attach != NULL) { | |
struct _ape_sock_callbacks *cb = ((struct _ape_sock_callbacks *)client->attach); | |
jsval js_callback = (cb->on_accept); | |
if (!JSVAL_IS_NULL(js_callback)) { | |
struct _ape_sock_callbacks *cbcopy; | |
JSObject *obj; | |
jsval params[1]; | |
cbcopy = xmalloc(sizeof(struct _ape_sock_callbacks)); | |
cbcopy->private = client; | |
cbcopy->asc = cb->asc; | |
JS_SetContextThread(cb->asc->cx); | |
JS_BeginRequest(cb->asc->cx); | |
obj = JS_NewObject(cb->asc->cx, &apesocket_class, NULL, NULL); | |
JS_AddRoot(cb->asc->cx, &obj); | |
JS_SetPrivate(cb->asc->cx, obj, cbcopy); | |
JS_DefineFunctions(cb->asc->cx, obj, apesocket_funcs); | |
params[0] = OBJECT_TO_JSVAL(obj); | |
JS_CallFunctionValue(cb->asc->cx, cb->asc->global, js_callback, 1, params, &rval); | |
JS_EndRequest(cb->asc->cx); | |
JS_ClearContextThread(cb->asc->cx); | |
} | |
} | |
} | |
/* Reporting error from JS compilation (parse error, etc...) */ | |
static void reportError(JSContext *cx, const char *message, JSErrorReport *report) | |
{ | |
fprintf(stderr, "%s:%u:%s\n", | |
report->filename ? report->filename : "<no filename>", | |
(unsigned int) report->lineno, | |
message); | |
} | |
static JSBool sockserver_addproperty(JSContext *cx, JSObject *obj, jsval idval, jsval *vp) | |
{ | |
if (JSVAL_IS_STRING(idval) && JS_TypeOfValue(cx, *vp) == JSTYPE_FUNCTION) { | |
ape_socket *socket = (ape_socket *)JS_GetPrivate(cx, obj); | |
char *prop = JS_GetStringBytes(JSVAL_TO_STRING(idval)); | |
if (strcasecmp(prop, "onaccept") == 0) { | |
((struct _ape_sock_callbacks *)socket->attach)->on_accept = *vp; | |
JS_AddRoot(cx, &((struct _ape_sock_callbacks *)socket->attach)->on_accept); | |
socket->callbacks.on_accept = sm_sock_onaccept; | |
} | |
} | |
return JS_TRUE; | |
} | |
APE_JS_NATIVE(ape_sm_addEvent) | |
//{ | |
const char *event; | |
ape_sm_callback *ascb; | |
*rval = JSVAL_NULL; | |
if (argc != 2) { | |
return JS_TRUE; | |
} | |
//JS_SetContextThread(cx); | |
//JS_BeginRequest(cx); | |
if (!JS_ConvertArguments(cx, argc-1, argv, "s", &event)) { | |
return JS_FALSE; | |
} | |
ascb = xmalloc(sizeof(*ascb)); | |
if (!JS_ConvertValue(cx, argv[1], JSTYPE_FUNCTION, &ascb->func)) { | |
free(ascb); | |
return JS_FALSE; | |
} | |
JS_AddRoot(cx, &ascb->func); | |
// JS_EndRequest(cx); | |
// JS_ClearContextThread(cx); | |
ascb->next = asc->callbacks; | |
ascb->callbackname = xstrdup(event); | |
asc->callbacks = ascb; | |
return JS_TRUE; | |
} | |
APE_JS_NATIVE(ape_sm_echo) | |
//{ | |
const char *string; | |
*rval = JSVAL_NULL; | |
//JS_SetContextThread(cx); | |
//JS_BeginRequest(cx); | |
if (!JS_ConvertArguments(cx, argc, argv, "s", &string)) { | |
return JS_FALSE; | |
} | |
//JS_EndRequest(cx); | |
//JS_ClearContextThread(cx); | |
printf("%s\n", string); | |
return JS_TRUE; | |
} | |
APE_JS_NATIVE_WP(ape_sm_sockserver_constructor) | |
//{ | |
int port; | |
char *ip; | |
ape_socket *server; | |
//JS_SetContextThread(cx); | |
//JS_BeginRequest(cx); | |
if (!JS_ConvertArguments(cx, argc, argv, "is", &port, &ip)) { | |
return JS_FALSE; | |
} | |
server = sock_listen(port, ip, g_ape); | |
server->attach = xmalloc(sizeof(struct _ape_sock_callbacks)); | |
((struct _ape_sock_callbacks *)server->attach)->on_accept = JSVAL_NULL; | |
((struct _ape_sock_callbacks *)server->attach)->asc = asc; | |
((struct _ape_sock_callbacks *)server->attach)->private = NULL; | |
// TODO : Other clalback | |
JS_SetPrivate(cx, obj, server); | |
// JS_EndRequest(cx); | |
// JS_ClearContextThread(cx); | |
return JS_TRUE; | |
} | |
static JSFunctionSpec ape_funcs[] = { | |
JS_FS("addEvent", ape_sm_addEvent, 2, 0, 0), /* Ape.addEvent('name', function() { }); */ | |
JS_FS("echo", ape_sm_echo, 1, 0, 0),/* Ape.echo('stdout\n'); */ | |
JS_FS_END | |
}; | |
static void ape_sm_define_ape(ape_sm_compiled *asc) | |
{ | |
JSObject *obj, *sock; | |
obj = JS_DefineObject(asc->cx, asc->global, "Ape", &ape_class, NULL, 0); | |
JS_SetPrivate(asc->cx, obj, asc); | |
JS_DefineFunctions(asc->cx, obj, ape_funcs); | |
sock = JS_InitClass(asc->cx, obj, NULL, &socketserver_class, ape_sm_sockserver_constructor, 2, NULL, NULL, NULL, NULL); | |
JS_SetPrivate(asc->cx, sock, asc); | |
} | |
static void ape_fire_callback(const char *name, acetables *g_ape) | |
{ | |
ape_sm_compiled *asc = ASMR->scripts; | |
if (asc == NULL) { | |
return; | |
} | |
while (asc != NULL) { | |
ape_sm_callback *cb; | |
for (cb = asc->callbacks; cb != NULL; cb = cb->next) { | |
if (strcasecmp(name, cb->callbackname) == 0) { | |
jsval rval; | |
JS_SetContextThread(asc->cx); | |
JS_BeginRequest(asc->cx); | |
JS_CallFunctionValue(asc->cx, asc->global, (cb->func), 0, NULL, &rval); | |
JS_EndRequest(asc->cx); | |
JS_ClearContextThread(asc->cx); | |
} | |
} | |
asc = asc->next; | |
} | |
} | |
static void init_module(acetables *g_ape) // Called when module is loaded | |
{ | |
JSRuntime *rt; | |
ape_sm_runtime *asr; | |
jsval rval; | |
int i; | |
glob_t globbuf; | |
rt = JS_NewRuntime(8L * 1024L * 1024L); // 8 Mio allocated | |
if (rt == NULL) { | |
printf("[ERR] Not enougth memory\n"); | |
exit(0); | |
} | |
asr = xmalloc(sizeof(*asr)); | |
asr->runtime = rt; | |
asr->scripts = NULL; | |
add_property(&g_ape->properties, "sm_runtime", asr, EXTEND_POINTER, EXTEND_ISPRIVATE); | |
glob("./scripts/*.ape.js", 0, NULL, &globbuf); | |
for (i = 0; i < globbuf.gl_pathc; i++) { | |
ape_sm_compiled *asc = xmalloc(sizeof(*asc)); | |
asc->filename = (void *)xstrdup(globbuf.gl_pathv[i]); | |
asc->cx = JS_NewContext(rt, 8192); | |
if (asc->cx == NULL) { | |
free(asc->filename); | |
free(asc); | |
continue; | |
} | |
JS_SetContextThread(asc->cx); | |
JS_BeginRequest(asc->cx); | |
JS_SetOptions(asc->cx, JSOPTION_VAROBJFIX); | |
JS_SetVersion(asc->cx, JSVERSION_LATEST); | |
JS_SetErrorReporter(asc->cx, reportError); | |
asc->global = JS_NewObject(asc->cx, &global_class, NULL, NULL); | |
JS_InitStandardClasses(asc->cx, asc->global); | |
/* define the Ape Object */ | |
ape_sm_define_ape(asc); | |
asc->bytecode = JS_CompileFile(asc->cx, asc->global, asc->filename); | |
if (asc->bytecode == NULL) { | |
JS_DestroyScript(asc->cx, asc->bytecode); | |
} else { | |
asc->scriptObj = JS_NewScriptObject(asc->cx, asc->bytecode); | |
/* Adding to the root (prevent the script to be GC collected) */ | |
JS_AddNamedRoot(asc->cx, asc->scriptObj, asc->filename); | |
/* put the Ape table on the script structure */ | |
asc->g_ape = g_ape; | |
asc->callbacks = NULL; | |
/* Run the script */ | |
JS_ExecuteScript(asc->cx, asc->global, asc->bytecode, &rval); | |
} | |
JS_EndRequest(asc->cx); | |
JS_ClearContextThread(asc->cx); | |
if (asc->bytecode == NULL) { | |
/* cleaning memory */ | |
} else { | |
asc->next = asr->scripts; | |
asr->scripts = asc; | |
} | |
} | |
globfree(&globbuf); | |
APE_JS_EVENT("init"); | |
} | |
static USERS *ape_cb_add_user(unsigned int fdclient, char *host, acetables *g_ape) | |
{ | |
USERS *n = adduser(fdclient, host, g_ape); | |
if (n != NULL) { | |
APE_JS_EVENT("adduser"); | |
} | |
return n; | |
} | |
static ace_callbacks callbacks = { | |
ape_cb_add_user, /* Called when new user is added */ | |
NULL, /* Called when a user is disconnected */ | |
NULL, /* Called when new chan is created */ | |
NULL, /* Called when a user join a channel */ | |
NULL /* Called when a user leave a channel */ | |
}; | |
APE_INIT_PLUGIN(MODULE_NAME, init_module, callbacks) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment