00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #ifndef lint
00021 static const char rcsid[] = "$Id: javascript.c,v 1.27 2005-12-28 17:27:31 sven Exp $";
00022 #endif
00023
00024 #include <stdio.h>
00025 #include <stdlib.h>
00026 #include <string.h>
00027
00028 #define XP_UNIX
00029
00030 #include <jsapi.h>
00031
00032 #include <eggdrop/eggdrop.h>
00033
00034
00035 typedef struct {
00036 JSContext *mycx;
00037 JSObject *myobj;
00038 JSFunction *command;
00039 char *name;
00040 } my_callback_cd_t;
00041
00042 static int my_command_handler(JSContext *cx, JSObject *obj, int argc, jsval *argv, jsval *rval);
00043 static int c_to_js_var(JSContext *cx, script_var_t *v, jsval *result);
00044 static int js_to_c_var(JSContext *cx, JSObject *obj, jsval val, script_var_t *var, int type);
00045 static int my_js_cb_delete(event_owner_t *owner, script_callback_t *me);
00046
00047
00048 static int my_load_script(void *ignore, char *fname);
00049 static int my_link_var(void *ignore, script_linked_var_t *var);
00050 static int my_unlink_var(void *ignore, script_linked_var_t *var);
00051 static int my_create_command(void *ignore, script_raw_command_t *info);
00052 static int my_delete_command(void *ignore, script_raw_command_t *info);
00053 static int my_get_arg(void *ignore, script_args_t *args, int num, script_var_t *var, int type);
00054
00055 static script_module_t my_script_interface = {
00056 "JavaScript", NULL,
00057 my_load_script,
00058 my_link_var, my_unlink_var,
00059 my_create_command, my_delete_command,
00060 my_get_arg
00061 };
00062
00063 static event_owner_t js_owner = {
00064 "javascript", 0,
00065 0, 0,
00066 0
00067 };
00068
00069 typedef struct {
00070 JSContext *cx;
00071 JSObject *obj;
00072 jsval *argv;
00073 } my_args_data_t;
00074
00075 static JSRuntime *global_js_runtime;
00076 static JSContext *global_js_context;
00077 static JSObject *global_js_object;
00078
00079 static char *error_logfile = NULL;
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109 static JSClass eggvar_class = {
00110 "EggdropVariable", JSCLASS_HAS_PRIVATE,
00111 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
00112 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
00113 };
00114
00115 static JSClass eggfunc_class = {
00116 "EggdropFunction", JSCLASS_HAS_PRIVATE,
00117 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
00118 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
00119 NULL, NULL,
00120 (JSNative) my_command_handler
00121 };
00122
00123 static JSClass global_class = {"global", JSCLASS_HAS_PRIVATE,
00124 JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,
00125 JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,JS_FinalizeStub
00126 };
00127
00128
00129 static int my_load_script(void *ignore, char *fname)
00130 {
00131 FILE *fp;
00132 int len;
00133 jsval rval;
00134 char *script;
00135
00136
00137 len = strlen(fname);
00138 if (len < 3 || fname[len-1] != 's' || fname[len-2] != 'j' || fname[len-3] != '.') {
00139
00140 return(0);
00141 }
00142
00143 fp = fopen(fname, "r");
00144 if (!fp) return(0);
00145
00146 fseek(fp, 0, SEEK_END);
00147 len = ftell(fp);
00148 fseek(fp, 0, SEEK_SET);
00149 script = (char *)malloc(len+1);
00150 fread(script, len, 1, fp);
00151 fclose(fp);
00152 script[len] = 0;
00153 JS_EvaluateScript(global_js_context, global_js_object, script, len, fname, 1, &rval);
00154 free(script);
00155
00156 return(0);
00157 }
00158
00159 static void linked_var_to_jsval(JSContext *cx, script_linked_var_t *var, jsval *val, script_var_t *script_val)
00160 {
00161 script_var_t script_var;
00162
00163 if (!script_val || !script_val->type) {
00164 script_var.type = var->type & SCRIPT_TYPE_MASK;
00165 script_var.len = -1;
00166 script_var.value = *(void **)var->value;
00167 script_val = &script_var;
00168 }
00169
00170 c_to_js_var(cx, script_val, val);
00171 }
00172
00173
00174 static int my_eggvar_set(JSContext *cx, JSObject *obj, int argc, jsval *argv, jsval *rval)
00175 {
00176 script_linked_var_t *linked_var;
00177 script_var_t script_var = {0};
00178
00179 if (argc != 1) return JS_FALSE;
00180
00181 linked_var = JS_GetPrivate(cx, obj);
00182 if (!linked_var) {
00183 putlog(LOG_MISC, "*", _("Don't create your own eggvars!"));
00184 return JS_FALSE;
00185 }
00186
00187 js_to_c_var(cx, obj, argv[0], &script_var, linked_var->type);
00188 script_linked_var_on_write(linked_var, &script_var);
00189 c_to_js_var(cx, &script_var, rval);
00190 return JS_TRUE;
00191 }
00192
00193
00194
00195 static int my_eggvar_value_of(JSContext *cx, JSObject *obj, int argc, jsval *argv, jsval *rval)
00196 {
00197 script_linked_var_t *linked_var;
00198
00199 linked_var = JS_GetPrivate(cx, obj);
00200 if (linked_var->callbacks && linked_var->callbacks->on_read) {
00201 script_var_t newval = {0};
00202 int r = (linked_var->callbacks->on_read)(linked_var, &newval);
00203 if (r) return JS_FALSE;
00204 linked_var_to_jsval(cx, linked_var, rval, &newval);
00205 }
00206 else linked_var_to_jsval(cx, linked_var, rval, NULL);
00207 return(JS_TRUE);
00208 }
00209
00210
00211 static int my_link_var(void *ignore, script_linked_var_t *var)
00212 {
00213 char *varname;
00214 JSObject *obj;
00215
00216 if (var->class && strlen(var->class)) varname = egg_mprintf("%s(%s)", var->class, var->name);
00217 else varname = strdup(var->name);
00218
00219 obj = JS_DefineObject(global_js_context, global_js_object, varname, &eggvar_class, NULL, JSPROP_ENUMERATE|JSPROP_EXPORTED|JSPROP_READONLY);
00220 if (!obj) {
00221 putlog(LOG_MISC, "*", "failed 1");
00222 return(0);
00223 }
00224 JS_SetPrivate(global_js_context, obj, var);
00225 JS_DefineFunction(global_js_context, obj, "toString", (JSNative) my_eggvar_value_of, 0, 0);
00226 JS_DefineFunction(global_js_context, obj, "valueOf", (JSNative) my_eggvar_value_of, 0, 0);
00227 JS_DefineFunction(global_js_context, obj, "set", (JSNative) my_eggvar_set, 0, 0);
00228 return(0);
00229 }
00230
00231
00232 static int my_unlink_var(void *ignore, script_linked_var_t *var)
00233 {
00234 char *varname;
00235
00236 if (var->class && strlen(var->class)) varname = egg_mprintf("%s(%s)", var->class, var->name);
00237 else varname = strdup(var->name);
00238
00239 JS_DeleteProperty(global_js_context, global_js_object, varname);
00240
00241 free(varname);
00242 return(0);
00243 }
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272 static int my_js_callbacker(script_callback_t *me, ...)
00273 {
00274 jsval *argv, result = 0;
00275 JSObject *myobj;
00276 JSContext *mycx;
00277 JSFunction *command;
00278 script_var_t var;
00279 my_callback_cd_t *cd;
00280 int i, n, retval = 0;
00281 va_list va;
00282
00283
00284 cd = (my_callback_cd_t *)me->callback_data;
00285
00286 if (me->syntax) n = strlen(me->syntax);
00287 else n = 0;
00288
00289 argv = (jsval *)malloc(n * sizeof(jsval));
00290 va_start(va, me);
00291 for (i = 0; i < n; i++) {
00292 var.type = me->syntax[i];
00293 if (var.type == SCRIPT_INTEGER || var.type == SCRIPT_UNSIGNED) var.value = (void *) (va_arg(va, int));
00294 else var.value = va_arg(va, void *);
00295 var.len = -1;
00296 c_to_js_var(cd->mycx, &var, argv+i);
00297 }
00298 va_end(va);
00299
00300 mycx = cd->mycx;
00301 myobj = cd->myobj;
00302 command = cd->command;
00303
00304 n = JS_CallFunction(mycx, myobj, command, n, argv, &result);
00305 free(argv);
00306 if (n) {
00307 int32 intval;
00308
00309 if (JS_ValueToInt32(mycx, result, &intval)) retval = intval;
00310 }
00311
00312 return(retval);
00313 }
00314
00315
00316 static int my_js_cb_delete(event_owner_t *owner, script_callback_t *me)
00317 {
00318 my_callback_cd_t *cd;
00319
00320 cd = (my_callback_cd_t *)me->callback_data;
00321 if (cd->name) free(cd->name);
00322 if (me->syntax) free(me->syntax);
00323 free(cd);
00324 free(me);
00325 return(0);
00326 }
00327
00328
00329 static int my_create_command(void *ignore, script_raw_command_t *info)
00330 {
00331 char *cmdname;
00332 JSObject *obj;
00333
00334 if (info->class && strlen(info->class)) cmdname = egg_mprintf("%s_%s", info->class, info->name);
00335 else cmdname = strdup(info->name);
00336
00337 obj = JS_DefineObject(global_js_context, global_js_object, cmdname, &eggfunc_class, NULL, JSPROP_ENUMERATE|JSPROP_EXPORTED|JSPROP_READONLY);
00338 if (!obj) return(0);
00339 free(cmdname);
00340 JS_SetPrivate(global_js_context, obj, info);
00341 return(0);
00342 }
00343
00344
00345 static int my_delete_command(void *ignore, script_raw_command_t *info)
00346 {
00347 char *cmdname;
00348
00349 if (info->class && strlen(info->class)) cmdname = egg_mprintf("%s_%s", info->class, info->name);
00350 else cmdname = strdup(info->name);
00351 JS_DeleteProperty(global_js_context, global_js_object, cmdname);
00352 free(cmdname);
00353 return(0);
00354 }
00355
00356
00357 static int c_to_js_var(JSContext *cx, script_var_t *v, jsval *result)
00358 {
00359 *result = JSVAL_VOID;
00360
00361
00362 if (v->type & SCRIPT_ARRAY) {
00363 JSObject *js_array;
00364 jsval element;
00365 int i;
00366
00367 js_array = JS_NewArrayObject(cx, 0, NULL);
00368
00369
00370 if ((v->type & SCRIPT_TYPE_MASK) == SCRIPT_VAR) {
00371 script_var_t **v_list;
00372
00373 v_list = (script_var_t **)v->value;
00374 for (i = 0; i < v->len; i++) {
00375 c_to_js_var(cx, v_list[i], &element);
00376 JS_SetElement(cx, js_array, i, &element);
00377 }
00378 }
00379 else {
00380
00381 script_var_t v_sub;
00382 void **values;
00383
00384 v_sub.type = v->type & (~SCRIPT_ARRAY);
00385 values = (void **)v->value;
00386 for (i = 0; i < v->len; i++) {
00387 v_sub.value = values[i];
00388 v_sub.len = -1;
00389 c_to_js_var(cx, &v_sub, &element);
00390 JS_SetElement(cx, js_array, i, &element);
00391 }
00392 }
00393
00394 if (v->type & SCRIPT_FREE) free(v->value);
00395 if (v->type & SCRIPT_FREE_VAR) free(v);
00396 *result = OBJECT_TO_JSVAL(js_array);
00397 return(0);
00398 }
00399
00400
00401 switch (v->type & SCRIPT_TYPE_MASK) {
00402 case SCRIPT_INTEGER:
00403 case SCRIPT_UNSIGNED:
00404 *result = INT_TO_JSVAL(((int) v->value));
00405 break;
00406 case SCRIPT_STRING: {
00407 char *str = v->value;
00408
00409 if (!str) str = "";
00410 if (v->len == -1) v->len = strlen(str);
00411 *result = STRING_TO_JSVAL(JS_NewStringCopyN(cx, str, v->len));
00412
00413 if (v->value && v->type & SCRIPT_FREE) free((char *)v->value);
00414 break;
00415 }
00416 case SCRIPT_STRING_LIST: {
00417 JSObject *js_array;
00418 jsval element;
00419 char **str = v->value;
00420 int i;
00421
00422 js_array = JS_NewArrayObject(cx, 0, NULL);
00423
00424 i = 0;
00425 while (str && *str) {
00426 element = STRING_TO_JSVAL(JS_NewStringCopyN(cx, *str, strlen(*str)));
00427 JS_SetElement(cx, js_array, i, &element);
00428 i++;
00429 str++;
00430 }
00431 *result = OBJECT_TO_JSVAL(js_array);
00432 break;
00433 }
00434 case SCRIPT_BYTES: {
00435 byte_array_t *bytes = v->value;
00436
00437 *result = STRING_TO_JSVAL(JS_NewStringCopyN(cx, bytes->bytes, bytes->len));
00438 if (bytes->do_free) free(bytes->bytes);
00439 if (v->type & SCRIPT_FREE) free(bytes);
00440 break;
00441 }
00442 case SCRIPT_POINTER: {
00443 char str[32];
00444
00445 sprintf(str, "#%u", (unsigned int) v->value);
00446 *result = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str));
00447 break;
00448 }
00449 case SCRIPT_USER: {
00450
00451 char *handle;
00452 user_t *u = v->value;
00453
00454 if (u) handle = u->handle;
00455 else handle = "*";
00456 *result = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, handle));
00457 break;
00458 }
00459 default:
00460
00461 *result = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, "Unsupported type"));
00462 }
00463 if (v->type & SCRIPT_FREE_VAR) free(v);
00464 return(0);
00465 }
00466
00467
00468
00469
00470 static int js_to_c_var(JSContext *cx, JSObject *obj, jsval val, script_var_t *var, int type)
00471 {
00472 int err = 0;
00473
00474 var->type = type;
00475 var->len = -1;
00476 var->value = NULL;
00477
00478 switch (type) {
00479 case SCRIPT_STRING: {
00480 JSString *str;
00481
00482 str = JS_ValueToString(cx, val);
00483 var->value = JS_GetStringBytes(str);
00484 var->len = JS_GetStringLength(str);
00485 break;
00486 }
00487 case SCRIPT_BYTES: {
00488 byte_array_t *byte_array;
00489 JSString *str;
00490
00491 byte_array = (byte_array_t *)malloc(sizeof(*byte_array));
00492
00493 str = JS_ValueToString(cx, val);
00494 byte_array->bytes = JS_GetStringBytes(str);
00495 byte_array->len = JS_GetStringLength(str);
00496
00497 var->value = byte_array;
00498 var->type |= SCRIPT_FREE;
00499 break;
00500 }
00501 case SCRIPT_UNSIGNED:
00502 case SCRIPT_INTEGER: {
00503 int32 intval;
00504
00505 err = !JS_ValueToInt32(cx, val, &intval);
00506 var->value = (void *)intval;
00507 break;
00508 }
00509 case SCRIPT_CALLBACK: {
00510 JSFunction *func;
00511 script_callback_t *cback;
00512 my_callback_cd_t *cdata;
00513
00514 func = JS_ValueToFunction(cx, val);
00515 if (!func) {
00516 err = 1;
00517 break;
00518 }
00519
00520 cback = (script_callback_t *)calloc(1, sizeof(*cback));
00521 cdata = (my_callback_cd_t *)calloc(1, sizeof(*cdata));
00522 cback->callback = (Function) my_js_callbacker;
00523 cback->callback_data = (void *)cdata;
00524 cback->name = strdup(JS_GetFunctionName(func));
00525 cback->owner = &js_owner;
00526
00527 cdata->mycx = cx;
00528 cdata->myobj = obj;
00529 cdata->command = func;
00530
00531 var->value = cback;
00532 break;
00533 }
00534 case SCRIPT_USER: {
00535 user_t *u;
00536 char *handle;
00537
00538 handle = JS_GetStringBytes(JS_ValueToString(cx, val));
00539 u = user_lookup_by_handle(handle);
00540 var->value = u;
00541 if (!u) {
00542 JS_ReportError(cx, _("User not found: %s"), handle);
00543 err = 1;
00544 }
00545 break;
00546 }
00547 default: {
00548 char vartype[2];
00549
00550 putlog(LOG_MISC, "*", _("Converting unknown! '%c'"), type);
00551 vartype[0] = type;
00552 vartype[1] = 0;
00553 JS_ReportError(cx, _("Cannot convert JS object to unknown variable type '%s'."), vartype);
00554 err = 1;
00555 }
00556 }
00557
00558 return(err);
00559 }
00560
00561
00562 static int my_command_handler(JSContext *cx, JSObject *obj, int argc, jsval *argv, jsval *rval)
00563 {
00564 script_raw_command_t *cmd;
00565 script_var_t retval;
00566 int err;
00567 JSObject *this_obj;
00568 script_args_t args;
00569 my_args_data_t argdata;
00570
00571
00572 *rval = JSVAL_VOID;
00573
00574
00575
00576 this_obj = JSVAL_TO_OBJECT(argv[-2]);
00577
00578
00579 cmd = (script_raw_command_t *)JS_GetPrivate(cx, this_obj);
00580
00581 argdata.cx = cx;
00582 argdata.obj = obj;
00583 argdata.argv = argv;
00584 args.module = &my_script_interface;
00585 args.client_data = &argdata;
00586 args.len = argc;
00587
00588 retval.type = 0;
00589 retval.value = NULL;
00590 retval.len = -1;
00591
00592 cmd->callback(cmd->client_data, &args, &retval);
00593
00594 err = retval.type & SCRIPT_ERROR;
00595 c_to_js_var(cx, &retval, rval);
00596
00597 if (err) return JS_FALSE;
00598 return JS_TRUE;
00599 }
00600
00601 static int my_get_arg(void *ignore, script_args_t *args, int num, script_var_t *var, int type)
00602 {
00603 my_args_data_t *argdata;
00604
00605 argdata = args->client_data;
00606 return js_to_c_var(argdata->cx, argdata->obj, argdata->argv[num], var, type);
00607
00608 }
00609
00610 static int javascript_init()
00611 {
00612
00613 global_js_runtime = JS_NewRuntime(0x100000);
00614 if (!global_js_runtime) return(1);
00615
00616
00617 global_js_context = JS_NewContext(global_js_runtime, 0x1000);
00618 if (!global_js_context) return(1);
00619
00620
00621 global_js_object = JS_NewObject(global_js_context, &global_class, NULL, NULL);
00622
00623
00624 JS_InitStandardClasses(global_js_context, global_js_object);
00625
00626
00627 JS_InitClass(global_js_context, global_js_object, NULL, &eggvar_class, NULL, 0, NULL, NULL, NULL, NULL);
00628
00629
00630 JS_InitClass(global_js_context, global_js_object, NULL, &eggfunc_class, NULL, 0, NULL, NULL, NULL, NULL);
00631
00632 return(0);
00633 }
00634
00635
00636 static int party_js(int pid, char *nick, user_t *u, char *cmd, char *text)
00637 {
00638 static int curline = 1;
00639 int retval;
00640 jsval js_rval;
00641
00642 if (!u || owner_check(u->handle)) {
00643 partyline_write(pid, _("You must be a permanent owner (defined in the config file) to use this command.\n"));
00644 return(BIND_RET_LOG);
00645 }
00646
00647 retval = JS_EvaluateScript(global_js_context, global_js_object, text, strlen(text), "console", curline++, &js_rval);
00648 if (!retval) partyline_printf(pid, _("JS Error: unknown for now\n"));
00649 else {
00650 JSString *str;
00651
00652 str = JS_ValueToString(global_js_context, js_rval);
00653 if (!str) partyline_printf(pid, "JS:\n");
00654 else partyline_printf(pid, "JS: %s\n", JS_GetStringBytes(str));
00655 }
00656 return(0);
00657 }
00658
00659 #if 0
00660 static void javascript_report(int idx, int details)
00661 {
00662 JSVersion version;
00663 const char *version_str;
00664
00665 version = JS_GetVersion(global_js_context);
00666 version_str = JS_VersionToString(version);
00667
00668 if (!details) {
00669 dprintf(idx, _("Using JavaScript version %s.\n"), version_str);
00670 return;
00671 }
00672
00673 dprintf(idx, _(" Using JavaScript version %s.\n"), version_str);
00674 }
00675 #endif
00676
00677 static bind_list_t party_commands[] = {
00678 {"js", party_js},
00679 {0}
00680 };
00681
00682 EXPORT_SCOPE int javascript_LTX_start(egg_module_t *modinfo);
00683 static char *javascript_close();
00684
00685 int javascript_LTX_start(egg_module_t *modinfo)
00686 {
00687 js_owner.module = modinfo;
00688
00689 modinfo->name = "javascript";
00690 modinfo->author = "eggdev";
00691 modinfo->version = "1.0.0";
00692 modinfo->description = "provides javascript scripting support";
00693 modinfo->close_func = javascript_close;
00694 javascript_init();
00695
00696 error_logfile = strdup("logs/javascript_errors.log");
00697
00698 script_register_module(&my_script_interface);
00699 script_playback(&my_script_interface);
00700
00701 bind_add_list("party", party_commands);
00702 return(0);
00703 }
00704
00705 static int javascript_close()
00706 {
00707 script_unregister_module(&my_script_interface);
00708 bind_rem_list("party", party_commands);
00709 module_undepend("javascript");
00710 return(0);
00711 }