Skip to content
Snippets Groups Projects
js_system.c 85 KiB
Newer Older
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(!illegal_filename(fname)
	                                          && allowed_filename(sys->cfg, fname)
	                                          && !trashcan(sys->cfg, fname, "file")));
	JS_RESUMEREQUEST(cx, rc);
	free(fname);

	return JS_TRUE;
}

static JSBool
js_safest_fname(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      fname = NULL;
	jsrefcount rc;
	if (argc < 1 || !JSVAL_IS_STRING(argv[0]))
		return JS_TRUE;

	JSVALUE_TO_MSTRING(cx, argv[0], fname, NULL);
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(safest_filename(fname)));
	JS_RESUMEREQUEST(cx, rc);
	free(fname);

	return JS_TRUE;
}

static JSBool
js_illegal_fname(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      fname = NULL;
	jsrefcount rc;
	if (argc < 1 || !JSVAL_IS_STRING(argv[0]))
		return JS_TRUE;

	JSVALUE_TO_MSTRING(cx, argv[0], fname, NULL);
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(illegal_filename(fname)));
	JS_RESUMEREQUEST(cx, rc);
	free(fname);

	return JS_TRUE;
}

static JSBool
js_allowed_fname(JSContext *cx, uintN argc, jsval *arglist)
{
	JSObject * obj = JS_THIS_OBJECT(cx, arglist);
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      fname = NULL;
	jsrefcount rc;
	if (argc < 1 || !JSVAL_IS_STRING(argv[0]))
		return JS_TRUE;

	js_system_private_t* sys;
	if ((sys = (js_system_private_t*)js_GetClassPrivate(cx, obj, &js_system_class)) == NULL)
		return JS_FALSE;

	JSVALUE_TO_MSTRING(cx, argv[0], fname, NULL);
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(allowed_filename(sys->cfg, fname)));
	JS_RESUMEREQUEST(cx, rc);
	free(fname);

	return JS_TRUE;
}

js_chkpid(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	int32      pid = 0;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if (!js_argc(cx, argc, 1))
	if (JSVAL_NULL_OR_VOID(argv[0])) {
		JS_ReportError(cx, "Invalid argument");
		return JS_FALSE;
	}
	JS_ValueToInt32(cx, argv[0], &pid);
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(check_pid(pid)));
js_killpid(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	int32      pid = 0;
	jsrefcount rc;
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if (!js_argc(cx, argc, 1))
	if (JSVAL_NULL_OR_VOID(argv[0])) {
		JS_ReportError(cx, "Invalid argument");
		return JS_FALSE;
	}
	JS_ValueToInt32(cx, argv[0], &pid);
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(terminate_pid(pid)));
static JSBool
js_text(JSContext *cx, uintN argc, jsval *arglist)
{
	JSObject* obj = JS_THIS_OBJECT(cx, arglist);
	jsval*    argv = JS_ARGV(cx, arglist);
	uint32    i = 0;
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);

	if (!js_argc(cx, argc, 1))
	if (JSVAL_NULL_OR_VOID(argv[0])) {
		JS_ReportError(cx, "Invalid argument");
		return JS_FALSE;
	}
	js_system_private_t* sys;
	if ((sys = (js_system_private_t*)js_GetClassPrivate(cx, obj, &js_system_class)) == NULL)
	if (sys->cfg == NULL || sys->cfg->text == NULL)
	if (JSVAL_IS_NUMBER(argv[0])) {
		if (!JS_ValueToECMAUint32(cx, argv[0], &i))
			return JS_FALSE;
	} else {
		JSString* js_str = JS_ValueToString(cx, argv[0]);
		JSSTRING_TO_MSTRING(cx, js_str, id, NULL);
		i = get_text_num(id) + 1; // Note: this is a non-caching look-up!
		free(id);
	}
	if (i > 0  && i <= TOTAL_TEXT) {
		JSString* js_str = JS_NewStringCopyZ(cx, sys->cfg->text[i - 1]);
			return JS_FALSE;
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
	}
	return JS_TRUE;
}
static jsSyncMethodSpec js_system_functions[] = {
	{"username",        js_username,        1,  JSTYPE_STRING,  JSDOCSTR("user_number")
	 , JSDOCSTR("Return name of user in specified user record <i>number</i>, or empty string if not found")
	 , 311},
	{"alias",           js_alias,           1,  JSTYPE_STRING,  JSDOCSTR("alias")
	 , JSDOCSTR("Return name of user that matches alias (if found in <tt>ctrl/alias.cfg</tt>)")
	 , 310},
	{"find_login_id",   js_find_login_id,   1,  JSTYPE_NUMBER,  JSDOCSTR("user-id")
	 , JSDOCSTR("Find a user's login ID (alias, real name, or number), returns matching user record number or 0 if not found")
	 , 32000},
	{"matchuser",       js_matchuser,       1,  JSTYPE_NUMBER,  JSDOCSTR("username [,sysop_alias=true]")
	 , JSDOCSTR("Exact user name matching, returns number of user whose name/alias matches <i>username</i> "
		        " or 0 if not found, matches well-known sysop aliases by default")
	 , 310},
	{"matchuserdata",   js_matchuserdata,   2,  JSTYPE_NUMBER,  JSDOCSTR("field, data [,<i>bool</i> match_del=false] [,<i>number</i> usernumber, <i>bool</i> match_next=false]")
	 , JSDOCSTR("Search user database for data in a specific field (see <tt>U_*</tt> in <tt>sbbsdefs.js</tt>).<br>"
		        "If <i>match_del</i> is <tt>true</tt>, deleted user records are searched, "
		        "returns first matching user record number, optional <i>usernumber</i> specifies user record to skip, "
		        "or record at which to begin searching if optional <i>match_next</i> is <tt>true</tt>.")
	 , 310},
	{"trashcan",        js_trashcan,        2,  JSTYPE_BOOLEAN, JSDOCSTR("basename, find_string")
	 , JSDOCSTR("Search <tt>text/<i>basename</i>.can</tt> for pseudo-regexp")
	 , 310},
	{"findstr",         js_findstr,         2,  JSTYPE_BOOLEAN, JSDOCSTR("path/filename or <i>array</i> of strings, find_string")
	 , JSDOCSTR("Search any trashcan/filter file or array of pattern strings (in <tt>*.can</tt> format) for <i>find_string</i>")
	 , 310},
	{"zonestr",         js_zonestr,         0,  JSTYPE_STRING,  JSDOCSTR("[timezone=<i>local</i>]")
	 , JSDOCSTR("Convert time zone integer to string, defaults to system timezone if <i>timezone</i> not specified")
	 , 310},
	{"timestr",         js_timestr,         0,  JSTYPE_STRING,  JSDOCSTR("[time=<i>current</i>]")
	 , JSDOCSTR("Convert time_t integer into a time string, "
		        "defaults to current time if <i>time</i> not specified")
	 , 310},
	{"datestr",         js_datestr,         0,  JSTYPE_STRING,  JSDOCSTR("[time=<i>current</i>]")
	 , JSDOCSTR("Convert time_t integer into a short (8 character) date string, in either numeric or verbal format (depending on system preference), "
		        "defaults to current date if <i>time</i> not specified. "
		        "If <i>time</i> is a string in numeric date format, returns the parsed time_t value as a number.")
	 , 310},
	{"secondstr",       js_secondstr,       0,  JSTYPE_STRING,  JSDOCSTR("seconds")
	 , JSDOCSTR("Convert elapsed time in seconds into a string in <tt>hh:mm:ss</tt> format")
	 , 310},
	{"spamlog",         js_spamlog,         6,  JSTYPE_BOOLEAN, JSDOCSTR("[protocol, action, reason, host, ip, to, from]")
	 , JSDOCSTR("Log a suspected SPAM attempt")
	 , 310},
	{"hacklog",         js_hacklog,         5,  JSTYPE_BOOLEAN, JSDOCSTR("[protocol, user, text, host, ip, port]")
	 , JSDOCSTR("Log a suspected hack attempt")
	 , 310},
	{"filter_ip",       js_filter_ip,       4,  JSTYPE_BOOLEAN, JSDOCSTR("[protocol, reason, host, ip, username, filename] [<i>number</i> duration-in-seconds]")
	 , JSDOCSTR("Add an IP address (with comment) to an IP filter file. If filename is not specified, the ip.can file is used")
	 , 311},
	{"get_node",        js_get_node,        1,  JSTYPE_OBJECT,  JSDOCSTR("node_number")
	 , JSDOCSTR("Read a node data record all at once (and leaving the record unlocked) "
		        "returning an object matching the elements of <tt>system.node_list</tt>")
	 , 31702},
	{"get_node_message", js_get_node_message, 0,  JSTYPE_STRING,  JSDOCSTR("node_number")
	 , JSDOCSTR("Read any messages waiting for the specified node and return in a single string")
	 , 311},
	{"put_node_message", js_put_node_message, 2,  JSTYPE_BOOLEAN, JSDOCSTR("node_number, message_text")
	 , JSDOCSTR("Send a node a short text message, delivered immediately")
	 , 310},
	{"get_telegram",    js_get_telegram,    1,  JSTYPE_STRING,  JSDOCSTR("user_number")
	 , JSDOCSTR("Return any short text messages waiting for the specified user")
	 , 311},
	{"put_telegram",    js_put_telegram,    2,  JSTYPE_BOOLEAN, JSDOCSTR("user_number, message_text")
	 , JSDOCSTR("Send a user a short text message, delivered immediately or during next logon")
	 , 310},
	{"notify",          js_notify,          2,  JSTYPE_BOOLEAN, JSDOCSTR("user_number, subject [,message_text]")
	 , JSDOCSTR("Notify a user or operator via both email and a short text message about an important event")
	 , 31801},
	{"newuser",         js_new_user,        1,  JSTYPE_ALIAS },
	{"new_user",        js_new_user,        1,  JSTYPE_OBJECT,  JSDOCSTR("name/alias [,client object]")
	 , JSDOCSTR("Create a new user record, returns a new <a href=#User_object>User object</a> representing the new user account, on success.<br>"
		        "returns an numeric error code on failure")
	 , 310},
	{"del_user",        js_del_user,        1,  JSTYPE_BOOLEAN, JSDOCSTR("user_number")
	 , JSDOCSTR("Delete the specified user account")
	 , 316},
	{"exec",            js_sys_exec,        0,  JSTYPE_NUMBER,  JSDOCSTR("command-line")
	 , JSDOCSTR("Execute a native system/shell command-line, returns <i>0</i> on success")
	 , 311},
	{"popen",           js_popen,           0,  JSTYPE_ARRAY,   JSDOCSTR("command-line")
	 , JSDOCSTR("Execute a native system/shell command-line, returns array of captured output lines on success "
		        "(<b>only functional on UNIX systems</b>)")
	 , 311},
	{"check_syspass",   js_chksyspass,      1,  JSTYPE_BOOLEAN, JSDOCSTR("password")
	 , JSDOCSTR("Compare the supplied <i>password</i> against the system password and returns <tt>true</tt> if it matches")
	 , 311},
	{"check_name",      js_chkname,         1,  JSTYPE_BOOLEAN, JSDOCSTR("name/alias")
	 , JSDOCSTR("Check that the provided name/alias string is suitable for a new user account, "
		        "returns <tt>true</tt> if it is valid")
	 , 315},
	{"check_filename",  js_chkfname,        1,  JSTYPE_BOOLEAN, JSDOCSTR("filename")
	 , JSDOCSTR("Verify that the specified <i>filename</i> string is legal and allowed for upload by users "
		        "(based on system configuration and filter files), returns <tt>true</tt> if the filename is allowed")
	 , 31902},
	{"allowed_filename", js_allowed_fname,  1,  JSTYPE_BOOLEAN, JSDOCSTR("filename")
	 , JSDOCSTR("Verify that the specified <i>filename</i> string is allowed for upload by users "
		        "(based on system configuration), returns <tt>true</tt> if the filename is allowed")
	 , 31902},
	{"safest_filename", js_safest_fname,    1,  JSTYPE_BOOLEAN, JSDOCSTR("filename")
	 , JSDOCSTR("Verify that the specified <i>filename</i> string contains only the safest subset of characters")
	 , 31902},
	{"illegal_filename", js_illegal_fname,  1,  JSTYPE_BOOLEAN, JSDOCSTR("filename")
	 , JSDOCSTR("Check if the specified <i>filename</i> string contains illegal characters or sequences, "
		        "returns <tt>true</tt> if it is an illegal filename")
	 , 31902},
	{"check_pid",       js_chkpid,          1,  JSTYPE_BOOLEAN, JSDOCSTR("process-ID")
	 , JSDOCSTR("Check that the provided process ID is a valid executing process on the system, "
		        "returns <tt>true</tt> if it is valid")
	 , 315},
	{"terminate_pid",   js_killpid,         1,  JSTYPE_BOOLEAN, JSDOCSTR("process-ID")
	 , JSDOCSTR("Terminate executing process on the system with the specified process ID, "
		        "returns <tt>true</tt> on success")
	 , 315},
	{"text",            js_text,            1,  JSTYPE_STRING,  JSDOCSTR("<i>number</i> index or <i>string</i> id")
	 , JSDOCSTR("Return specified text string (see <tt>bbs.text()</tt> for details)<br>"
		        "The <i>string</i> id support was added in v3.20.")
	 , 31802},
/* node properties */
enum {
	/* raw node_t fields */
	NODE_PROP_STATUS
	, NODE_PROP_VSTATUS
	, NODE_PROP_ERRORS
	, NODE_PROP_ACTION
	, NODE_PROP_ACTIVITY
	, NODE_PROP_USERON
	, NODE_PROP_CONNECTION
	, NODE_PROP_MISC
	, NODE_PROP_AUX
	, NODE_PROP_EXTAUX
	, NODE_PROP_DIR
static const char* node_prop_desc[] = {
	"Status (see <tt>nodedefs.js</tt> for valid values)"
	, "Verbal status - <small>READ ONLY</small>"
	, "Error counter"
	, "Current user action (see <tt>nodedefs.js</tt>)"
	, "Current user activity - <small>READ ONLY</small>y"
	, "Current user number"
	, "Connection speed (<tt>0xffff</tt> = Telnet or RLogin)"
	, "Miscellaneous bit-flags (see <tt>nodedefs.js</tt>)"
	, "Auxiliary value"
	, "Extended auxiliary value"
	, "Node directory - <small>READ ONLY</small>"
	, NULL
static JSBool js_node_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
	char       tmp[128];
	jsval      idval;
	uint       node_num;
	jsint      tiny;
	node_t     node;
	JSObject*  sysobj;
	JSObject*  node_list;
	jsrefcount rc;
	JSString*  js_str;
	JS_IdToValue(cx, id, &idval);
	tiny = JSVAL_TO_INT(idval);
	if ((node_list = JS_GetParent(cx, obj)) == NULL)
	if ((sysobj = JS_GetParent(cx, node_list)) == NULL)
	if ((sys = (js_system_private_t*)js_GetClassPrivate(cx, sysobj, &js_system_class)) == NULL)
	node_num = (uintptr_t)JS_GetPrivate(cx, obj) >> 1;
	rc = JS_SUSPENDREQUEST(cx);
	memset(&node, 0, sizeof(node));
	if (getnodedat(sys->cfg, node_num, &node, /* lockit: */ FALSE, &sys->nodefile) != 0) {
		case NODE_PROP_STATUS:
			*vp = INT_TO_JSVAL((int)node.status);
			break;
			if ((js_str = JS_NewStringCopyZ(cx, node_vstatus(sys->cfg, &node, tmp, sizeof tmp))) == NULL)
			*vp = STRING_TO_JSVAL(js_str);
			break;
			*vp = INT_TO_JSVAL((int)node.errors);
			break;
			*vp = INT_TO_JSVAL((int)node.action);
			break;
			if ((js_str = JS_NewStringCopyZ(cx, node_activity(sys->cfg, &node, tmp, sizeof tmp, node_num))) == NULL)
			*vp = STRING_TO_JSVAL(js_str);
			break;
			*vp = INT_TO_JSVAL((int)node.useron);
			break;
		case NODE_PROP_CONNECTION:
			*vp = INT_TO_JSVAL((int)node.connection);
			break;
			*vp = INT_TO_JSVAL((int)node.misc);
			break;
			*vp = INT_TO_JSVAL((int)node.aux);
			break;
			*vp = UINT_TO_JSVAL(node.extaux);
			if ((js_str = JS_NewStringCopyZ(cx, sys->cfg->node_path[node_num - 1])) == NULL)
			*vp = STRING_TO_JSVAL(js_str);
			break;
static JSBool js_node_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
	jsval      idval;
	uint       node_num;
	jsint      val = 0;
	jsint      tiny;
	node_t     node;
	JSObject*  sysobj;
	JSObject*  node_list;
	jsrefcount rc;

	if ((node_list = JS_GetParent(cx, obj)) == NULL)
	if ((sysobj = JS_GetParent(cx, node_list)) == NULL)
	if ((sys = (js_system_private_t*)js_GetClassPrivate(cx, sysobj, &js_system_class)) == NULL)
	node_num = (uintptr_t)JS_GetPrivate(cx, obj) >> 1;
	rc = JS_SUSPENDREQUEST(cx);
	memset(&node, 0, sizeof(node));
	if (getnodedat(sys->cfg, node_num, &node, /* lockit: */ TRUE, &sys->nodefile)) {
	if (JSVAL_IS_NUMBER(*vp))
		JS_ValueToInt32(cx, *vp, &val);
	JS_IdToValue(cx, id, &idval);
	tiny = JSVAL_TO_INT(idval);
	rc = JS_SUSPENDREQUEST(cx);
		case NODE_PROP_STATUS:
			node.status = (BYTE)val;
			node.errors = (BYTE)val;
			node.action = (BYTE)val;
			node.useron = (WORD)val;
			break;
		case NODE_PROP_CONNECTION:
			node.connection = (WORD)val;
			node.misc = (WORD)val;
			node.aux = (WORD)val;
	putnodedat(sys->cfg, node_num, &node, /* closeit: */ FALSE, sys->nodefile);
	mqtt_putnodedat(sys->mqtt, node_num, &node);
static jsSyncPropertySpec js_node_properties[] = {
/*		 name,						tinyid,					flags,								ver	*/

/* raw node_t fields */
	{   "status",                   NODE_PROP_STATUS,       JSPROP_ENUMERATE,                   310 },
	{   "vstatus",                  NODE_PROP_VSTATUS,      JSPROP_ENUMERATE | JSPROP_READONLY,   320 },
	{   "errors",                   NODE_PROP_ERRORS,       JSPROP_ENUMERATE,                   310 },
	{   "action",                   NODE_PROP_ACTION,       JSPROP_ENUMERATE,                   310 },
	{   "activity",                 NODE_PROP_ACTIVITY,     JSPROP_ENUMERATE | JSPROP_READONLY,   320 },
	{   "useron",                   NODE_PROP_USERON,       JSPROP_ENUMERATE,                   310 },
	{   "connection",               NODE_PROP_CONNECTION,   JSPROP_ENUMERATE,                   310 },
	{   "misc",                     NODE_PROP_MISC,         JSPROP_ENUMERATE,                   310 },
	{   "aux",                      NODE_PROP_AUX,          JSPROP_ENUMERATE,                   310 },
	{   "extaux",                   NODE_PROP_EXTAUX,       JSPROP_ENUMERATE,                   310 },
	{   "dir",                      NODE_PROP_DIR,          JSPROP_ENUMERATE | JSPROP_READONLY,   315 },
static JSBool js_node_resolve(JSContext *cx, JSObject *obj, jsid id)
	char*  name = NULL;
	JSBool ret;
	if (id != JSID_VOID && id != JSID_EMPTY) {
deuce's avatar
deuce committed
		jsval idval;
deuce's avatar
deuce committed
		JS_IdToValue(cx, id, &idval);
		if (JSVAL_IS_STRING(idval)) {
deuce's avatar
deuce committed
			JSSTRING_TO_MSTRING(cx, JSVAL_TO_STRING(idval), name, NULL);
			HANDLE_PENDING(cx, name);
deuce's avatar
deuce committed
	}
	ret = js_SyncResolve(cx, obj, name, js_node_properties, NULL, NULL, 0);
	if (name)
deuce's avatar
deuce committed
		free(name);
deuce's avatar
deuce committed
}

static JSBool js_node_enumerate(JSContext *cx, JSObject *obj)
{
	return js_node_resolve(cx, obj, JSID_VOID);
static JSClass js_node_class = {
	"Node"                  /* name			*/
	, JSCLASS_HAS_PRIVATE    /* flags		*/
	, JS_PropertyStub        /* addProperty	*/
	, JS_PropertyStub        /* delProperty	*/
	, js_node_get            /* getProperty	*/
	, js_node_set            /* setProperty	*/
	, js_node_enumerate      /* enumerate	*/
	, js_node_resolve        /* resolve		*/
	, JS_ConvertStub         /* convert		*/
	, JS_FinalizeStub        /* finalize		*/
deuce's avatar
deuce committed
#define LAZY_INTEGER(PropName, PropValue) \
		if (name == NULL || strcmp(name, (PropName)) == 0) { \
			val = UINT_TO_JSVAL((PropValue)); \
			JS_DefineProperty(cx, obj, (PropName), val, NULL, NULL, JSPROP_ENUMERATE); \
			if (name) { \
				free(name); \
deuce's avatar
deuce committed
#define LAZY_STRING(PropName, PropValue) \
		if (name == NULL || strcmp(name, (PropName)) == 0) { \
			if ((js_str = JS_NewStringCopyZ(cx, (PropValue))) != NULL) { \
				JS_DefineProperty(cx, obj, PropName, STRING_TO_JSVAL(js_str), NULL, NULL, JSPROP_ENUMERATE); \
				if (name) { \
					free(name); \
deuce's avatar
deuce committed
				free(name); \
deuce's avatar
deuce committed
			} \
deuce's avatar
deuce committed
#define LAZY_STRFUNC(PropName, Function, PropValue) \
		if (name == NULL || strcmp(name, (PropName)) == 0) { \
			Function; \
			if ((js_str = JS_NewStringCopyZ(cx, (PropValue))) != NULL) { \
				JS_DefineProperty(cx, obj, PropName, STRING_TO_JSVAL(js_str), NULL, NULL, JSPROP_ENUMERATE); \
				if (name) { \
					free(name); \
deuce's avatar
deuce committed
				free(name); \
deuce's avatar
deuce committed
			} \
deuce's avatar
deuce committed
#define LAZY_STRFUNC_TRUNCSP(PropName, Function, PropValue) \
		if (name == NULL || strcmp(name, (PropName)) == 0) { \
			Function; \
			if ((js_str = JS_NewStringCopyZ(cx, truncsp(PropValue))) != NULL) { \
				JS_DefineProperty(cx, obj, PropName, STRING_TO_JSVAL(js_str), NULL, NULL, JSPROP_ENUMERATE); \
				if (name) { \
					free(name); \
deuce's avatar
deuce committed
				free(name); \
deuce's avatar
deuce committed
			} \
static JSBool js_system_resolve(JSContext *cx, JSObject *obj, jsid id)
	char*     name = NULL;
	jsval     val;
	char      str[256];
	JSString* js_str;
	JSBool    ret;
	JSObject* newobj;
	JSObject* nodeobj;
	int       i;
	if (id != JSID_VOID && id != JSID_EMPTY) {
deuce's avatar
deuce committed
		jsval idval;
deuce's avatar
deuce committed
		JS_IdToValue(cx, id, &idval);
		if (JSVAL_IS_STRING(idval)) {
deuce's avatar
deuce committed
			JSSTRING_TO_MSTRING(cx, JSVAL_TO_STRING(idval), name, NULL);
			HANDLE_PENDING(cx, name);
deuce's avatar
deuce committed
	}
	/****************************/
	/* static string properties */
deuce's avatar
deuce committed
	LAZY_STRING("version", VERSION);
	LAZY_STRFUNC("revision", sprintf(str, "%c", REVISION), str);
deuce's avatar
deuce committed
	LAZY_STRFUNC_TRUNCSP("beta_version", SAFECOPY(str, beta_version), str);
	if (name == NULL || strcmp(name, "full_version") == 0) {
		sprintf(str, "%s%c%s", VERSION, REVISION, beta_version);
deuce's avatar
deuce committed
		truncsp(str);
#if defined(_DEBUG)
		strcat(str, " Debug");
		if ((js_str = JS_NewStringCopyZ(cx, str)) != NULL) {
deuce's avatar
deuce committed
			val = STRING_TO_JSVAL(js_str);
			JS_SetProperty(cx, obj, "full_version", &val);
			if (name)
				return JS_TRUE;
		else if (name)
			return JS_TRUE;
deuce's avatar
deuce committed
	LAZY_STRING("version_notice", VERSION_NOTICE);
deuce's avatar
deuce committed
	LAZY_INTEGER("version_num", VERSION_NUM);
	LAZY_INTEGER("version_hex", VERSION_HEX);
	/* Git repo details */
	LAZY_STRING("git_branch", git_branch);
	LAZY_STRING("git_hash", git_hash);
	LAZY_STRING("git_date", git_date);
	LAZY_INTEGER("git_time", (uint32_t)git_time);
deuce's avatar
deuce committed
	LAZY_STRING("platform", PLATFORM_DESC);
	LAZY_STRING("architecture", ARCHITECTURE_DESC);
	LAZY_STRFUNC("msgbase_lib", sprintf(str, "SMBLIB %s", smb_lib_ver()), str);
deuce's avatar
deuce committed
	LAZY_STRFUNC("compiled_with", DESCRIBE_COMPILER(str), str);
	LAZY_STRFUNC("compiled_when", sprintf(str, "%s %.5s", __DATE__, __TIME__), str);
deuce's avatar
deuce committed
	LAZY_STRING("copyright", COPYRIGHT_NOTICE);
	LAZY_STRING("js_version", (char *)JS_GetImplementationVersion());
	LAZY_STRING("os_version", os_version(str, sizeof(str)));
deuce's avatar
deuce committed
	/* fido_addr_list property */
	if (name == NULL || strcmp(name, "fido_addr_list") == 0) {
		if ((sys = (js_system_private_t*)js_GetClassPrivate(cx, obj, &js_system_class)) == NULL)
		if ((newobj = JS_NewArrayObject(cx, 0, NULL)) == NULL)
		if (!JS_SetParent(cx, newobj, obj))
		if (!JS_DefineProperty(cx, obj, "fido_addr_list", OBJECT_TO_JSVAL(newobj)
		                       , NULL, NULL, JSPROP_ENUMERATE))
		for (i = 0; i < sys->cfg->total_faddrs; i++) {
			val = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, smb_faddrtoa(&sys->cfg->faddr[i], str)));
deuce's avatar
deuce committed
			JS_SetElement(cx, newobj, i, &val);
		}
		if (name)
			return JS_TRUE;
	if (name == NULL || strcmp(name, "stats") == 0) {
		if ((sys = (js_system_private_t*)js_GetClassPrivate(cx, obj, &js_system_class)) == NULL)
deuce's avatar
deuce committed
		newobj = JS_DefineObject(cx, obj, "stats", &js_sysstats_class, NULL
		                         , JSPROP_ENUMERATE | JSPROP_READONLY);
#ifdef BUILD_JSDOCS
		js_DescribeSyncObject(cx, newobj, "System statistics", 310);
		js_CreateArrayOfStrings(cx, newobj, "_property_desc_list", sysstat_prop_desc, JSPROP_READONLY);
#endif
		if (name)
			return JS_TRUE;
deuce's avatar
deuce committed
	/* node_list property */
	if (name == NULL || strcmp(name, "node_list") == 0) {
		if ((sys = (js_system_private_t*)js_GetClassPrivate(cx, obj, &js_system_class)) == NULL)
		if ((newobj = JS_NewArrayObject(cx, 0, NULL)) == NULL)
		if (!JS_SetParent(cx, newobj, obj))
		if (!JS_DefineProperty(cx, obj, "node_list", OBJECT_TO_JSVAL(newobj)
		                       , NULL, NULL, JSPROP_ENUMERATE))
		for (i = 0; i < sys->cfg->sys_nodes && i < sys->cfg->sys_lastnode; i++) {
deuce's avatar
deuce committed
			nodeobj = JS_NewObject(cx, &js_node_class, NULL, newobj);

			if (nodeobj == NULL)
deuce's avatar
deuce committed

			/* Store node number */
			/* We have to shift it to make it look like a pointer to JS. :-( */
			if (!JS_SetPrivate(cx, nodeobj, (char*)(((uintptr_t)i + 1) << 1)))
deuce's avatar
deuce committed
	#ifdef BUILD_JSDOCS
			if (i == 0) {
				js_DescribeSyncObject(cx, nodeobj, "Terminal Server node listing", 310);
deuce's avatar
deuce committed
				js_CreateArrayOfStrings(cx, nodeobj, "_property_desc_list", node_prop_desc, JSPROP_READONLY);
			}
	#endif

			val = OBJECT_TO_JSVAL(nodeobj);
			if (!JS_SetElement(cx, newobj, i, &val))
		if (name)
			return JS_TRUE;
deuce's avatar
deuce committed
	ret = js_SyncResolve(cx, obj, name, js_system_properties, js_system_functions, NULL, 0);
deuce's avatar
deuce committed
	return ret;
deuce's avatar
deuce committed
static JSBool js_system_enumerate(JSContext *cx, JSObject *obj)
{
	return js_system_resolve(cx, obj, JSID_VOID);
static void js_system_finalize(JSContext *cx, JSObject *obj)
{
	if ((sys = (js_system_private_t*)JS_GetPrivate(cx, obj)) == NULL)
	CLOSE_OPEN_FILE(sys->nodefile);
	free(sys);
	JS_SetPrivate(cx, obj, NULL);
JSBool js_CreateTextProperties(JSContext* cx, JSObject* parent)
{
	jsval val;

	if (!JS_GetProperty(cx, parent, "text", &val))
		return JS_FALSE;

	JSObject* text = JSVAL_TO_OBJECT(val);
	for (int i = 0; i < TOTAL_TEXT; ++i) {
		val = INT_TO_JSVAL(i + 1);
		if (!JS_SetProperty(cx, text, text_id[i], &val))
	"System"                /* name			*/
	, JSCLASS_HAS_PRIVATE    /* flags		*/
	, JS_PropertyStub        /* addProperty	*/
	, JS_PropertyStub        /* delProperty	*/
	, js_system_get          /* getProperty	*/
	, js_system_set          /* setProperty	*/
	, js_system_enumerate    /* enumerate	*/
	, js_system_resolve      /* resolve		*/
	, JS_ConvertStub         /* convert		*/
	, js_system_finalize     /* finalize		*/
JSObject* js_CreateSystemObject(JSContext* cx, JSObject* parent
                                , scfg_t* cfg, time_t uptime, char* host_name, char* socklib_desc, struct mqtt* mqtt)
	jsval     val;
	JSObject* sysobj;
	JSString* js_str;
	char      str[256];
deuce's avatar
deuce committed
	sysobj = JS_DefineObject(cx, parent, "system", &js_system_class, NULL
	                         , JSPROP_ENUMERATE | JSPROP_READONLY);
	if (sysobj == NULL)
	if ((sys = calloc(sizeof(*sys), 1)) == NULL)
	if (!JS_SetPrivate(cx, sysobj, sys))
deuce's avatar
deuce committed
	/****************************/
	/* static string properties */
	if ((js_str = JS_NewStringCopyZ(cx, host_name)) == NULL)
deuce's avatar
deuce committed
	val = STRING_TO_JSVAL(js_str);
	if (!JS_SetProperty(cx, sysobj, "host_name", &val))
	if ((js_str = JS_NewStringCopyZ(cx, socklib_version(str, sizeof str, socklib_desc))) == NULL)
deuce's avatar
deuce committed
	val = STRING_TO_JSVAL(js_str);
	if (!JS_SetProperty(cx, sysobj, "socket_lib", &val))
deuce's avatar
deuce committed
	/***********************/
	val = DOUBLE_TO_JSVAL((double)uptime);
	if (!JS_SetProperty(cx, sysobj, "uptime", &val))
	js_DescribeSyncObject(cx, sysobj, "Global system-related properties and methods", 310);
deuce's avatar
deuce committed
	js_CreateArrayOfStrings(cx, sysobj, "_property_desc_list", sys_prop_desc, JSPROP_READONLY);
#endif  /* JAVSCRIPT */