Skip to content
Snippets Groups Projects
js_bbs.cpp 138 KiB
Newer Older
/* Synchronet JavaScript "bbs" Object */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
 *																			*
 * This program 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.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
#include "filedat.h"

#ifdef JAVASCRIPT

/*****************************/
/* BBS Object Properites */
/*****************************/
enum {
	BBS_PROP_SYS_STATUS
	, BBS_PROP_STARTUP_OPT
	, BBS_PROP_ANSWER_TIME
	, BBS_PROP_LOGON_TIME
	, BBS_PROP_START_TIME
	, BBS_PROP_NS_TIME
	, BBS_PROP_LAST_NS_TIME
	, BBS_PROP_ONLINE
	, BBS_PROP_TIMELEFT
	, BBS_PROP_EVENT_TIME
	, BBS_PROP_EVENT_CODE

	, BBS_PROP_FIRST_NODE
	, BBS_PROP_LAST_NODE

	, BBS_PROP_NODE_NUM
	, BBS_PROP_NODE_SETTINGS
	, BBS_PROP_NODE_STATUS
	, BBS_PROP_NODE_ERRORS
	, BBS_PROP_NODE_ACTION
	, BBS_PROP_NODE_USERON
	, BBS_PROP_NODE_CONNECTION
	, BBS_PROP_NODE_MISC
	, BBS_PROP_NODE_AUX
	, BBS_PROP_NODE_EXTAUX
	, BBS_PROP_NODE_VAL_USER

	, BBS_PROP_LOGON_ULB
	, BBS_PROP_LOGON_DLB
	, BBS_PROP_LOGON_ULS
	, BBS_PROP_LOGON_DLS
	, BBS_PROP_LOGON_POSTS
	, BBS_PROP_LOGON_EMAILS
	, BBS_PROP_LOGON_FBACKS
	, BBS_PROP_POSTS_READ

	, BBS_PROP_MENU_DIR
	, BBS_PROP_MENU_FILE
	, BBS_PROP_MAIN_CMDS
	, BBS_PROP_FILE_CMDS

	, BBS_PROP_CURGRP
	, BBS_PROP_CURSUB
	, BBS_PROP_CURSUB_CODE
	, BBS_PROP_CURLIB
	, BBS_PROP_CURDIR
	, BBS_PROP_CURDIR_CODE

	, BBS_PROP_CONNECTION        /* READ ONLY */
	, BBS_PROP_RLOGIN_NAME
	, BBS_PROP_RLOGIN_PASS
	, BBS_PROP_RLOGIN_TERM
	, BBS_PROP_CLIENT_NAME

	, BBS_PROP_ERRORLEVEL        /* READ ONLY */
	, BBS_PROP_SMB_GROUP
	, BBS_PROP_SMB_GROUP_DESC
	, BBS_PROP_SMB_GROUP_NUM
	, BBS_PROP_SMB_SUB
	, BBS_PROP_SMB_SUB_DESC
	, BBS_PROP_SMB_SUB_CODE
	, BBS_PROP_SMB_SUB_NUM
	, BBS_PROP_SMB_ATTR
	, BBS_PROP_SMB_LAST_MSG
	, BBS_PROP_SMB_TOTAL_MSGS
	, BBS_PROP_SMB_MSGS
	, BBS_PROP_SMB_CURMSG    // writable
	, BBS_PROP_MSG_TO
	, BBS_PROP_MSG_TO_EXT
	, BBS_PROP_MSG_TO_NET
	, BBS_PROP_MSG_TO_AGENT
	, BBS_PROP_MSG_FROM
	, BBS_PROP_MSG_FROM_EXT
	, BBS_PROP_MSG_FROM_NET
	, BBS_PROP_MSG_FROM_BBSID
	, BBS_PROP_MSG_FROM_AGENT
	, BBS_PROP_MSG_REPLYTO
	, BBS_PROP_MSG_REPLYTO_EXT
	, BBS_PROP_MSG_REPLYTO_NET
	, BBS_PROP_MSG_REPLYTO_AGENT
	, BBS_PROP_MSG_SUBJECT
	, BBS_PROP_MSG_DATE
	, BBS_PROP_MSG_TIMEZONE
	, BBS_PROP_MSG_DATE_IMPORTED
	, BBS_PROP_MSG_ATTR
	, BBS_PROP_MSG_AUXATTR
	, BBS_PROP_MSG_NETATTR
	, BBS_PROP_MSG_OFFSET
	, BBS_PROP_MSG_NUMBER    // writable
	, BBS_PROP_MSG_EXPIRATION
	, BBS_PROP_MSG_FORWARDED
	, BBS_PROP_MSG_THREAD_ID
	, BBS_PROP_MSG_THREAD_BACK
	, BBS_PROP_MSG_THREAD_NEXT
	, BBS_PROP_MSG_THREAD_FIRST
	, BBS_PROP_MSG_ID
	, BBS_PROP_MSG_REPLY_ID
	, BBS_PROP_MSG_DELIVERY_ATTEMPTS

	, BBS_PROP_MSGHDR_TOS
	, BBS_PROP_DOWNLOAD_CPS
	, BBS_PROP_BATCH_UPLOAD_TOTAL
	, BBS_PROP_BATCH_DNLOAD_TOTAL
	/* READ ONLY */
	, BBS_PROP_FILE_NAME
	, BBS_PROP_FILE_DESC
	, BBS_PROP_FILE_DIR
	, BBS_PROP_FILE_ATTR
	, BBS_PROP_FILE_DATE
	, BBS_PROP_FILE_SIZE
	, BBS_PROP_FILE_CREDITS
	, BBS_PROP_FILE_ULER
	, BBS_PROP_FILE_DATE_ULED
	, BBS_PROP_FILE_DATE_DLED
	, BBS_PROP_FILE_TIMES_DLED

	, BBS_PROP_COMMAND_STR
static const char* bbs_prop_desc[] = {
	"System status bit-flags (see <tt>SS_*</tt> in <tt>sbbsdefs.js</tt> for bit definitions)"
	, "Startup options bit-flags (see <tt>BBS_OPT_*</tt> in <tt>sbbsdefs.js</tt> for bit definitions)"
	, "Answer time, in <i>time_t</i> format"
	, "Logon time, in <i>time_t</i> format"
	, "Time from which user's time left is calculated, in <i>time_t</i> format"
	, "Current file new-scan time, in <i>time_t</i> format"
	, "Previous file new-scan time, in <i>time_t</i> format"
	, "Online (see <tt>ON_*</tt> in <tt>sbbsdefs.js</tt> for valid values)"
	, "Time left (in seconds)"
	, "Time of next exclusive event (in <i>time_t</i> format), or 0 if none"
	, "Internal code of next exclusive event"

	, "First node number (of this instance of Synchronet)"
	, "Last node number (of this instance of Synchronet)"

	, "Current node number"
	, "Current node settings bit-flags (see <tt>NM_*</tt> in <tt>sbbsdefs.js</tt> for bit definitions)"
	, "Current node status value (see <tt>nodedefs.js</tt> for valid values)"
	, "Current node error counter"
	, "Current node action (see <tt>nodedefs.js</tt> for valid values)"
	, "Current node user number (<i>useron</i> value)"
	, "Current node connection type (see <tt>nodedefs.js</tt> for valid values)"
	, "Current node misc value (see <tt>nodedefs.js</tt> for valid values)"
	, "Current node aux value"
	, "Current node extended aux (<i>extaux</i>) value"
	, "Validation feedback user for this node (or 0 for no validation feedback required)"

	, "Bytes uploaded during this session"
	, "Bytes downloaded during this session"
	, "Files uploaded during this session"
	, "Files downloaded during this session"
	, "Messages posted during this session"
	, "E-mails sent during this session"
	, "Feedback messages sent during this session"
	, "Messages read during this session"

	, "Menu subdirectory (overrides default)"
	, "Menu file (overrides default)"
	, "Total main menu commands received from user during this session"
	, "Total file menu commands received from user during this session"

	, "Current message group"
	, "Current message sub-board"
	, "Current message sub-board internal code"
	, "Current file library"
	, "Current file directory"
	, "Current file directory internal code"

	, "Remote connection type"
	, "Login name given during RLogin negotiation"
	, "Password specified during RLogin negotiation"
	, "Terminal specified during RLogin negotiation"
	, "Client name"

	, "Error level returned from last executed external program"
	/* READ ONLY */
	, "Message group name of message being read"
	, "Message group description of message being read"
	, "Message group number of message being read"
	, "Sub-board name of message being read"
	, "Sub-board description of message being read"
	, "Sub-board internal code of message being read"
	, "Sub-board number of message being read"
	, "Message base attributes"
	, "Highest message number in message base"
	, "Total number of messages in message base"
	, "Number of messages loaded from message base"
	, "Current message number in message base"

	/* READ ONLY */
	, "Message recipient name"
	, "Message recipient extension"
	, "Message recipient network address"
	, "Message recipient agent type"
	, "Message sender name"
	, "Message sender extension"
	, "Message sender network address"
	, "Message sender BBS ID"
	, "Message sender agent type"
	, "Message reply-to name"
	, "Message reply-to extension"
	, "Message reply-to network address"
	, "Message reply-to agent type"
	, "Message subject"
	, "Message date/time"
	, "Message time zone"
	, "Message date/time imported"
	, "Message attributes"
	, "Message auxiliary attributes"
	, "Message network attributes"
	, "Message header offset"
	, "Message number (unique, monotonically incrementing)"
	, "Message expiration"
	, "Message forwarded"
	, "Message thread identifier (0 if unknown)"
	, "Message thread, back message number"
	, "Message thread, next message number"
	, "Message thread, message number of first reply to this message"
	, "Message identifier"
	, "Message replied-to identifier"
	, "Message delivery attempt counter"

	, "Message header displayed at top-of-screen"

	, "File name"
	, "File description"
	, "File directory (number)"
	, "File attribute flags"
	, "File date"
	, "File size (in bytes)"
	, "File credit value"
	, "File uploader (user name)"
	, "File upload date"
	, "File last-download date"
	, "File download count"

	, "Most recent file download rate (in characters/bytes per second)"
	, "Number of files in batch upload queue"
	, "Number of files in batch download queue"

	, "Current command shell/module <i>command string</i> value"
	, NULL
};
extern JSClass     js_bbs_class; // defined later
static sbbs_t *js_GetPrivate(JSContext *cx, JSObject *obj)
	return (sbbs_t *)js_GetClassPrivate(cx, obj, &js_bbs_class);
static JSBool js_bbs_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
	jsval       idval;
	char        tmp[128];
	const char* p = NULL;
	const char* nulstr = "";
	uint32      val = 0;
	jsint       tiny;
	sbbs_t*     sbbs;
	jsrefcount  rc;
	if ((sbbs = js_GetPrivate(cx, obj)) == NULL)
		return(JS_FALSE);

	JS_IdToValue(cx, id, &idval);
	tiny = JSVAL_TO_INT(idval);
		case BBS_PROP_SYS_STATUS:
			val = sbbs->sys_status;
			break;
		case BBS_PROP_STARTUP_OPT:
			val = sbbs->startup->options;
			break;
		case BBS_PROP_ANSWER_TIME:
			val = (uint32)sbbs->answertime;
			break;
		case BBS_PROP_LOGON_TIME:
			val = (uint32)sbbs->logontime;
			val = (uint32)sbbs->starttime;
		case BBS_PROP_NS_TIME:
			val = (uint32)sbbs->ns_time;
			break;
		case BBS_PROP_LAST_NS_TIME:
			val = (uint32)sbbs->last_ns_time;
		case BBS_PROP_ONLINE:
			val = sbbs->online;
		case BBS_PROP_TIMELEFT:
			rc = JS_SUSPENDREQUEST(cx);
			val = sbbs->gettimeleft(false);
			val = (uint32)sbbs->event_time;
			p = sbbs->event_code;
		case BBS_PROP_FIRST_NODE:
			val = sbbs->startup->first_node;
			break;
		case BBS_PROP_LAST_NODE:
			val = sbbs->startup->last_node;
			break;

		case BBS_PROP_NODE_NUM:
			val = sbbs->cfg.node_num;
rswindell's avatar
rswindell committed
		case BBS_PROP_NODE_SETTINGS:
			val = sbbs->cfg.node_misc;
rswindell's avatar
rswindell committed
		case BBS_PROP_NODE_STATUS:
			val = sbbs->thisnode.status;
rswindell's avatar
rswindell committed
			break;
		case BBS_PROP_NODE_ERRORS:
			val = sbbs->thisnode.errors;
rswindell's avatar
rswindell committed
			break;
rswindell's avatar
rswindell committed
		case BBS_PROP_NODE_ACTION:
			val = sbbs->action;
rswindell's avatar
rswindell committed
			break;
rswindell's avatar
rswindell committed
		case BBS_PROP_NODE_USERON:
			val = sbbs->thisnode.useron;
rswindell's avatar
rswindell committed
			break;
		case BBS_PROP_NODE_CONNECTION:
			val = sbbs->thisnode.connection;
rswindell's avatar
rswindell committed
			break;
		case BBS_PROP_NODE_MISC:
			val = sbbs->thisnode.misc;
rswindell's avatar
rswindell committed
			break;
		case BBS_PROP_NODE_AUX:
			val = sbbs->thisnode.aux;
rswindell's avatar
rswindell committed
			break;
		case BBS_PROP_NODE_EXTAUX:
			val = sbbs->thisnode.extaux;
rswindell's avatar
rswindell committed
			break;

		case BBS_PROP_NODE_VAL_USER:
			val = sbbs->cfg.valuser;
			break;

		case BBS_PROP_LOGON_ULB:
			val = (uint32_t)sbbs->logon_ulb;  // TODO: fix for > 4GB!
			break;
		case BBS_PROP_LOGON_DLB:
			val = (uint32_t)sbbs->logon_dlb;  // TODO: fix for > 4GB!
			break;
		case BBS_PROP_LOGON_ULS:
			val = (uint32_t)sbbs->logon_uls;  // TODO: fix for > 4GB!
			break;
		case BBS_PROP_LOGON_DLS:
			val = (uint32_t)sbbs->logon_dls;  // TODO: fix for > 4GB!
			break;
		case BBS_PROP_LOGON_POSTS:
			val = sbbs->logon_posts;
			break;
		case BBS_PROP_LOGON_EMAILS:
			val = sbbs->logon_emails;
			break;
		case BBS_PROP_LOGON_FBACKS:
			val = sbbs->logon_fbacks;
			break;
		case BBS_PROP_POSTS_READ:
			val = sbbs->posts_read;
			break;

		case BBS_PROP_MENU_DIR:
			p = sbbs->menu_dir;
		case BBS_PROP_MENU_FILE:
			p = sbbs->menu_file;
		case BBS_PROP_MAIN_CMDS:
			val = sbbs->main_cmds;
rswindell's avatar
rswindell committed
		case BBS_PROP_FILE_CMDS:
			val = sbbs->xfer_cmds;
			val = sbbs->curgrp;
			break;
		case BBS_PROP_CURSUB:
			if (sbbs->curgrp < sbbs->usrgrps)
				val = sbbs->cursub[sbbs->curgrp];
		case BBS_PROP_CURSUB_CODE:
			if (sbbs->subnum_is_valid(sbbs->cursubnum))
				p = sbbs->cfg.sub[sbbs->cursubnum]->code;
			val = sbbs->curlib;
			break;
		case BBS_PROP_CURDIR:
			if (sbbs->curlib < sbbs->usrlibs)
				val = sbbs->curdir[sbbs->curlib];
		case BBS_PROP_CURDIR_CODE:
			if (sbbs->dirnum_is_valid(sbbs->curdirnum))
				p = sbbs->cfg.dir[sbbs->curdirnum]->code;
		case BBS_PROP_CONNECTION:
			p = sbbs->connection;
		case BBS_PROP_RLOGIN_NAME:
			p = sbbs->rlogin_name;
		case BBS_PROP_RLOGIN_PASS:
			p = sbbs->rlogin_pass;
		case BBS_PROP_RLOGIN_TERM:
			p = sbbs->rlogin_term;
		case BBS_PROP_CLIENT_NAME:
			p = sbbs->client_name;
			val = sbbs->errorlevel;
		/* Currently Open Message Base (sbbs.smb) */
		case BBS_PROP_SMB_GROUP:
			if (!subnum_is_valid(&sbbs->cfg, sbbs->smb.subnum))
				p = nulstr;
				p = sbbs->cfg.grp[sbbs->cfg.sub[sbbs->smb.subnum]->grp]->sname;
			if (!subnum_is_valid(&sbbs->cfg, sbbs->smb.subnum))
				p = nulstr;
				p = sbbs->cfg.grp[sbbs->cfg.sub[sbbs->smb.subnum]->grp]->lname;
			if (sbbs->subnum_is_valid(sbbs->smb.subnum)) {
				for (ugrp = 0; ugrp < sbbs->usrgrps; ugrp++)
					if (sbbs->usrgrp[ugrp] == sbbs->cfg.sub[sbbs->smb.subnum]->grp)
			if (!subnum_is_valid(&sbbs->cfg, sbbs->smb.subnum))
				p = nulstr;
				p = sbbs->cfg.sub[sbbs->smb.subnum]->sname;
			if (!subnum_is_valid(&sbbs->cfg, sbbs->smb.subnum))
				p = nulstr;
				p = sbbs->cfg.sub[sbbs->smb.subnum]->lname;
			if (!subnum_is_valid(&sbbs->cfg, sbbs->smb.subnum))
				p = nulstr;
				p = sbbs->cfg.sub[sbbs->smb.subnum]->code;
			if (sbbs->usrsubs && sbbs->subnum_is_valid(sbbs->smb.subnum)) {
				for (ugrp = 0; ugrp < sbbs->usrgrps; ugrp++)
					if (sbbs->usrgrp[ugrp] == sbbs->cfg.sub[sbbs->smb.subnum]->grp)
				for (usub = 0; usub < sbbs->usrsubs[ugrp]; usub++)
					if (sbbs->usrsub[ugrp][usub] == sbbs->smb.subnum)
			val = sbbs->smb.status.attr;
			val = sbbs->smb.status.last_msg;
			val = sbbs->smb.status.total_msgs;
			val = sbbs->smb.msgs;
			break;
		case BBS_PROP_SMB_CURMSG:
			val = sbbs->smb.curmsg;

		/* Currently Displayed Message Header (sbbs.current_msg) */
		case BBS_PROP_MSG_TO:
			if (sbbs->current_msg_to == NULL)
				p = nulstr;
				p = sbbs->current_msg_to;
			if (sbbs->current_msg == NULL || sbbs->current_msg->to_ext == NULL)
				p = nulstr;
				p = sbbs->current_msg->to_ext;
			if (sbbs->current_msg == NULL || sbbs->current_msg->to_net.type == NET_NONE)
				p = nulstr;
				p = smb_netaddrstr(&sbbs->current_msg->to_net, tmp);
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->to_agent;
			if (sbbs->current_msg_from == NULL)
				p = nulstr;
				p = sbbs->current_msg_from;
			if (sbbs->current_msg == NULL || sbbs->current_msg->from_ext == NULL)
				p = nulstr;
				p = sbbs->current_msg->from_ext;
			if (sbbs->current_msg == NULL || sbbs->current_msg->from_net.type == NET_NONE)
				p = nulstr;
				p = smb_netaddrstr(&sbbs->current_msg->from_net, tmp);
		case BBS_PROP_MSG_FROM_BBSID:
			if (sbbs->current_msg == NULL || sbbs->current_msg->ftn_bbsid == NULL)
				p = nulstr;
			else // Should we return only the last ID of the QWKnet route here?
				p = sbbs->current_msg->ftn_bbsid;
			break;
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->from_agent;
			if (sbbs->current_msg == NULL || sbbs->current_msg->replyto == NULL)
				p = nulstr;
				p = sbbs->current_msg->replyto;
			if (sbbs->current_msg == NULL || sbbs->current_msg->replyto_ext == NULL)
				p = nulstr;
				p = sbbs->current_msg->replyto_ext;
			if (sbbs->current_msg == NULL || sbbs->current_msg->replyto_net.type == NET_NONE)
				p = nulstr;
				p = smb_netaddrstr(&sbbs->current_msg->replyto_net, tmp);
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->replyto_agent;
			if (sbbs->current_msg_subj == NULL)
				p = nulstr;
				p = sbbs->current_msg_subj;
			if (sbbs->current_msg != NULL)
				val = (uint32)smb_time(sbbs->current_msg->hdr.when_written);
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->hdr.when_written.zone;
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->hdr.when_imported.time;
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->hdr.attr;
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->hdr.auxattr;
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->hdr.netattr;
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->idx_offset;
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->hdr.number;
				val = sbbs->current_msg_number;
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->expiration;
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->forwarded;
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->hdr.thread_id;
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->hdr.thread_back;
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->hdr.thread_next;
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->hdr.thread_first;
			break;
		case BBS_PROP_MSG_DELIVERY_ATTEMPTS:
			if (sbbs->current_msg != NULL)
				val = sbbs->current_msg->hdr.delivery_attempts;
			if (sbbs->current_msg == NULL || sbbs->current_msg->id == NULL)
				p = nulstr;
				p = sbbs->current_msg->id;
			break;
		case BBS_PROP_MSG_REPLY_ID:
			if (sbbs->current_msg == NULL || sbbs->current_msg->reply_id == NULL)
				p = nulstr;
				p = sbbs->current_msg->reply_id;
		case BBS_PROP_MSGHDR_TOS:
			val = sbbs->msghdr_tos;
			break;
		/* Currently Displayed File (sbbs.current_file) */
		case BBS_PROP_FILE_NAME:
			if (sbbs->current_file == NULL)
				p = nulstr;
				p = sbbs->current_file->name;
			break;
		case BBS_PROP_FILE_DESC:
			if (sbbs->current_file == NULL)
				p = nulstr;
				p = sbbs->current_file->desc;
			break;
		case BBS_PROP_FILE_ULER:
			if (sbbs->current_file == NULL)
				p = nulstr;
				p = sbbs->current_file->from;
			break;
		case BBS_PROP_FILE_DATE:
			if (sbbs->current_file == NULL)
				p = nulstr;
				val = (uint32)sbbs->current_file->time;
			break;
		case BBS_PROP_FILE_DATE_ULED:
			if (sbbs->current_file == NULL)
				p = nulstr;
				val = sbbs->current_file->hdr.when_imported.time;
			break;
		case BBS_PROP_FILE_DATE_DLED:
			if (sbbs->current_file == NULL)
				p = nulstr;
				val = sbbs->current_file->hdr.last_downloaded;
			break;
		case BBS_PROP_FILE_TIMES_DLED:
			if (sbbs->current_file == NULL)
				p = nulstr;
				val = sbbs->current_file->hdr.times_downloaded;
			break;
		case BBS_PROP_FILE_SIZE:
			if (sbbs->current_file == NULL)
				p = nulstr;
			else // TODO: fix for 64-bit file sizes
				val = (uint32)sbbs->current_file->size;
			break;
		case BBS_PROP_FILE_CREDITS:
			if (sbbs->current_file == NULL)
				p = nulstr;
				val = (uint32_t)sbbs->current_file->cost; // TODO (cost is now 64-bit)
			break;
		case BBS_PROP_FILE_DIR:
			if (sbbs->current_file == NULL)
				p = nulstr;
				val = sbbs->current_file->dir;
			break;
		case BBS_PROP_FILE_ATTR:
			if (sbbs->current_file == NULL)
				p = nulstr;
				val = sbbs->current_file->hdr.attr;
		case BBS_PROP_DOWNLOAD_CPS:
			val = sbbs->cur_cps;
			break;
			break;
		case BBS_PROP_BATCH_DNLOAD_TOTAL:
			p = sbbs->main_csi.str;
		default:
			return(JS_TRUE);
	}
	if (p != NULL) {
		JSString* js_str = JS_NewStringCopyZ(cx, p);
		if (js_str == NULL)
			return(JS_FALSE);
		*vp = STRING_TO_JSVAL(js_str);
	} else
		*vp = UINT_TO_JSVAL(val);
static JSBool js_bbs_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
	jsval     idval;
	char*     p = NULL;
	uint32    val = 0;
	jsint     tiny;
	JSString* js_str;
	sbbs_t*   sbbs;
	if ((sbbs = js_GetPrivate(cx, obj)) == NULL)
		return(JS_FALSE);

	JS_IdToValue(cx, id, &idval);
	tiny = JSVAL_TO_INT(idval);
	if (JSVAL_IS_NUMBER(*vp) || JSVAL_IS_BOOLEAN(*vp)) {
		if (!JS_ValueToECMAUint32(cx, *vp, &val))
	else if (JSVAL_IS_STRING(*vp)) {
		if ((js_str = JS_ValueToString(cx, *vp)) == NULL)
rswindell's avatar
rswindell committed
			return(JS_FALSE);
deuce's avatar
deuce committed
		JSSTRING_TO_MSTRING(cx, js_str, p, NULL);
		HANDLE_PENDING(cx, p);
rswindell's avatar
rswindell committed
	}
		case BBS_PROP_SYS_STATUS:
			sbbs->sys_status = val;
			break;
		case BBS_PROP_STARTUP_OPT:
			sbbs->startup->options = val;
			break;
		case BBS_PROP_ANSWER_TIME:
			sbbs->answertime = val;
			break;
		case BBS_PROP_LOGON_TIME:
			sbbs->logontime = val;
			sbbs->starttime = val;
		case BBS_PROP_NS_TIME:
			sbbs->ns_time = val;
			break;
		case BBS_PROP_LAST_NS_TIME:
			sbbs->last_ns_time = val;
		case BBS_PROP_ONLINE:
			sbbs->online = val;
rswindell's avatar
rswindell committed
		case BBS_PROP_NODE_SETTINGS:
			sbbs->cfg.node_misc = val;
rswindell's avatar
rswindell committed
		case BBS_PROP_NODE_ACTION:
			sbbs->action = (uchar)val;
rswindell's avatar
rswindell committed
			break;
		case BBS_PROP_NODE_VAL_USER:
			sbbs->cfg.valuser = (ushort)val;
			break;
		case BBS_PROP_LOGON_ULB:
			sbbs->logon_ulb = val;
			break;
		case BBS_PROP_LOGON_DLB:
			sbbs->logon_dlb = val;
			break;
		case BBS_PROP_LOGON_ULS:
			sbbs->logon_uls = val;
			break;
		case BBS_PROP_LOGON_DLS:
			sbbs->logon_dls = val;
			break;
		case BBS_PROP_LOGON_POSTS:
			sbbs->logon_posts = val;
			break;
		case BBS_PROP_LOGON_EMAILS:
			sbbs->logon_emails = val;
		case BBS_PROP_LOGON_FBACKS:
			sbbs->logon_fbacks = val;
		case BBS_PROP_POSTS_READ:
			sbbs->posts_read = val;
		case BBS_PROP_MENU_DIR:
			if (p != NULL)
				SAFECOPY(sbbs->menu_dir, p);
		case BBS_PROP_MENU_FILE:
			if (p != NULL)
				SAFECOPY(sbbs->menu_file, p);
		case BBS_PROP_MAIN_CMDS:
			sbbs->main_cmds = val;
rswindell's avatar
rswindell committed
		case BBS_PROP_FILE_CMDS:
			sbbs->xfer_cmds = val;
		case BBS_PROP_SMB_CURMSG:
			sbbs->smb.curmsg = val;
			break;
		case BBS_PROP_MSG_NUMBER:
			sbbs->current_msg_number = val;
			break;
			if (p != NULL) {   /* set by name */
				for (i = 0; i < sbbs->usrgrps; i++)
					if (!stricmp(sbbs->cfg.grp[sbbs->usrgrp[i]]->sname, p))
				if (i < sbbs->usrgrps)
					sbbs->curgrp = i;
			if ((int)val < sbbs->cfg.total_grps && (int)val < sbbs->usrgrps)
				sbbs->curgrp = val;
			break;
		case BBS_PROP_CURSUB:
		case BBS_PROP_CURSUB_CODE:
			if (p != NULL) {   /* set by code */
				for (int i = 0; i < sbbs->usrgrps; i++)
					for (int j = 0; j < sbbs->usrsubs[i]; j++)
						if (!stricmp(sbbs->cfg.sub[sbbs->usrsub[i][j]]->code, p)) {
							sbbs->curgrp = i;
							sbbs->cursub[i] = j;
			if (sbbs->curgrp < sbbs->cfg.total_grps && (int)val < sbbs->usrsubs[sbbs->curgrp])
				sbbs->cursub[sbbs->curgrp] = val;
			break;
		case BBS_PROP_CURLIB:
			if (p != NULL) {   /* set by name */
				for (i = 0; i < sbbs->usrlibs; i++)
					if (!stricmp(sbbs->cfg.lib[sbbs->usrlib[i]]->sname, p))
				if (i < sbbs->usrlibs)
					sbbs->curlib = i;
			if ((int)val < sbbs->cfg.total_libs && (int)val < sbbs->usrlibs)
				sbbs->curlib = val;
			break;
		case BBS_PROP_CURDIR:
		case BBS_PROP_CURDIR_CODE:
			if (p != NULL) {   /* set by code */
				for (int i = 0; i < sbbs->usrlibs; i++)
					for (int j = 0; j < sbbs->usrdirs[i]; j++)
						if (!stricmp(sbbs->cfg.dir[sbbs->usrdir[i][j]]->code, p)) {
							sbbs->curlib = i;
							sbbs->curdir[i] = j;
			if (sbbs->curlib < sbbs->cfg.total_libs && (int)val < sbbs->usrdirs[sbbs->curlib])
				sbbs->curdir[sbbs->curlib] = val;
		case BBS_PROP_RLOGIN_NAME:
			if (p != NULL)
				SAFECOPY(sbbs->rlogin_name, p);
		case BBS_PROP_RLOGIN_PASS:
			if (p != NULL)
				SAFECOPY(sbbs->rlogin_pass, p);
		case BBS_PROP_RLOGIN_TERM:
			if (p != NULL)
				SAFECOPY(sbbs->rlogin_term, p);
		case BBS_PROP_CLIENT_NAME:
			if (p != NULL)
				SAFECOPY(sbbs->client_name, p);
				strlcpy(sbbs->main_csi.str, p, 1024);
		default:
deuce's avatar
deuce committed
				free(p);
			return(JS_TRUE);
	}

deuce's avatar
deuce committed
		free(p);

	if (sbbs->usrgrps)
		sbbs->cursubnum = sbbs->usrsub[sbbs->curgrp][sbbs->cursub[sbbs->curgrp]];   /* Used for ARS */
		sbbs->cursubnum = INVALID_SUB;
	if (sbbs->usrlibs)
		sbbs->curdirnum = sbbs->usrdir[sbbs->curlib][sbbs->curdir[sbbs->curlib]];   /* Used for ARS */
		sbbs->curdirnum = INVALID_DIR;
	return(JS_TRUE);
}

#define PROP_READONLY JSPROP_ENUMERATE | JSPROP_READONLY
static jsSyncPropertySpec js_bbs_properties[] = {
/*		 name				,tinyid					,flags				,ver	*/

	{   "sys_status", BBS_PROP_SYS_STATUS, JSPROP_ENUMERATE, 310},
	{   "startup_options", BBS_PROP_STARTUP_OPT, JSPROP_ENUMERATE, 310},
	{   "answer_time", BBS_PROP_ANSWER_TIME, JSPROP_ENUMERATE, 310},
	{   "logon_time", BBS_PROP_LOGON_TIME, JSPROP_ENUMERATE, 310},
	{   "start_time", BBS_PROP_START_TIME, JSPROP_ENUMERATE, 314},
	{   "new_file_time", BBS_PROP_NS_TIME, JSPROP_ENUMERATE, 310},
	{   "last_new_file_time", BBS_PROP_LAST_NS_TIME, JSPROP_ENUMERATE, 310},
	{   "online", BBS_PROP_ONLINE, JSPROP_ENUMERATE, 310},
	{   "timeleft", BBS_PROP_TIMELEFT, JSPROP_READONLY, 310},                   /* alias */
	{   "time_left", BBS_PROP_TIMELEFT, PROP_READONLY, 311},
	{   "event_time", BBS_PROP_EVENT_TIME, PROP_READONLY, 311},
	{   "event_code", BBS_PROP_EVENT_CODE, PROP_READONLY, 311},
	{   "first_node", BBS_PROP_FIRST_NODE, PROP_READONLY, 320},
	{   "last_node", BBS_PROP_LAST_NODE, PROP_READONLY, 320},
	{   "node_num", BBS_PROP_NODE_NUM, PROP_READONLY, 310},
	{   "node_settings", BBS_PROP_NODE_SETTINGS, JSPROP_ENUMERATE, 310},
	{   "node_status", BBS_PROP_NODE_STATUS, PROP_READONLY, 31700},
	{   "node_errors", BBS_PROP_NODE_ERRORS, PROP_READONLY, 31700},
	{   "node_action", BBS_PROP_NODE_ACTION, JSPROP_ENUMERATE, 310},
	{   "node_useron", BBS_PROP_NODE_USERON, PROP_READONLY, 31700},
	{   "node_connection", BBS_PROP_NODE_CONNECTION, PROP_READONLY, 31700},
	{   "node_misc", BBS_PROP_NODE_MISC, PROP_READONLY, 31700},
	{   "node_aux", BBS_PROP_NODE_AUX, PROP_READONLY, 31700},
	{   "node_extaux", BBS_PROP_NODE_EXTAUX, PROP_READONLY, 31700},
	{   "node_val_user", BBS_PROP_NODE_VAL_USER, JSPROP_ENUMERATE, 310},
	{   "logon_ulb", BBS_PROP_LOGON_ULB, JSPROP_ENUMERATE, 310},
	{   "logon_dlb", BBS_PROP_LOGON_DLB, JSPROP_ENUMERATE, 310},
	{   "logon_uls", BBS_PROP_LOGON_ULS, JSPROP_ENUMERATE, 310},
	{   "logon_dls", BBS_PROP_LOGON_DLS, JSPROP_ENUMERATE, 310},
	{   "logon_posts", BBS_PROP_LOGON_POSTS, JSPROP_ENUMERATE, 310},
	{   "logon_emails", BBS_PROP_LOGON_EMAILS, JSPROP_ENUMERATE, 310},
	{   "logon_fbacks", BBS_PROP_LOGON_FBACKS, JSPROP_ENUMERATE, 310},
	{   "posts_read", BBS_PROP_POSTS_READ, JSPROP_ENUMERATE, 310},
	{   "menu_dir", BBS_PROP_MENU_DIR, JSPROP_ENUMERATE, 310},
	{   "menu_file", BBS_PROP_MENU_FILE, JSPROP_ENUMERATE, 310},
	{   "main_cmds", BBS_PROP_MAIN_CMDS, JSPROP_ENUMERATE, 310},
	{   "file_cmds", BBS_PROP_FILE_CMDS, JSPROP_ENUMERATE, 310},
	{   "curgrp", BBS_PROP_CURGRP, JSPROP_ENUMERATE, 310},
	{   "cursub", BBS_PROP_CURSUB, JSPROP_ENUMERATE, 310},
	{   "cursub_code", BBS_PROP_CURSUB_CODE, JSPROP_ENUMERATE, 314},
	{   "curlib", BBS_PROP_CURLIB, JSPROP_ENUMERATE, 310},
	{   "curdir", BBS_PROP_CURDIR, JSPROP_ENUMERATE, 310},
	{   "curdir_code", BBS_PROP_CURDIR_CODE, JSPROP_ENUMERATE, 314},
	{   "connection", BBS_PROP_CONNECTION, PROP_READONLY, 310},
	{   "rlogin_name", BBS_PROP_RLOGIN_NAME, JSPROP_ENUMERATE, 310},
	{   "rlogin_password", BBS_PROP_RLOGIN_PASS, JSPROP_ENUMERATE, 315},
	{   "rlogin_terminal", BBS_PROP_RLOGIN_TERM, JSPROP_ENUMERATE, 316},
	{   "client_name", BBS_PROP_CLIENT_NAME, JSPROP_ENUMERATE, 310},
	{   "errorlevel", BBS_PROP_ERRORLEVEL, PROP_READONLY, 312},

	{   "smb_group", BBS_PROP_SMB_GROUP, PROP_READONLY, 310},
	{   "smb_group_desc", BBS_PROP_SMB_GROUP_DESC, PROP_READONLY, 310},
	{   "smb_group_number", BBS_PROP_SMB_GROUP_NUM, PROP_READONLY, 310},
	{   "smb_sub", BBS_PROP_SMB_SUB, PROP_READONLY, 310},
	{   "smb_sub_desc", BBS_PROP_SMB_SUB_DESC, PROP_READONLY, 310},
	{   "smb_sub_code", BBS_PROP_SMB_SUB_CODE, PROP_READONLY, 310},
	{   "smb_sub_number", BBS_PROP_SMB_SUB_NUM, PROP_READONLY, 310},
	{   "smb_attr", BBS_PROP_SMB_ATTR, PROP_READONLY, 310},
	{   "smb_last_msg", BBS_PROP_SMB_LAST_MSG, PROP_READONLY, 310},
	{   "smb_total_msgs", BBS_PROP_SMB_TOTAL_MSGS, PROP_READONLY, 310},
	{   "smb_msgs", BBS_PROP_SMB_MSGS, PROP_READONLY, 310},
	{   "smb_curmsg", BBS_PROP_SMB_CURMSG, JSPROP_ENUMERATE, 310},

	{   "msg_to", BBS_PROP_MSG_TO, PROP_READONLY, 310},
	{   "msg_to_ext", BBS_PROP_MSG_TO_EXT, PROP_READONLY, 310},
	{   "msg_to_net", BBS_PROP_MSG_TO_NET, PROP_READONLY, 310},
	{   "msg_to_agent", BBS_PROP_MSG_TO_AGENT, PROP_READONLY, 310},
	{   "msg_from", BBS_PROP_MSG_FROM, PROP_READONLY, 310},
	{   "msg_from_ext", BBS_PROP_MSG_FROM_EXT, PROP_READONLY, 310},
	{   "msg_from_net", BBS_PROP_MSG_FROM_NET, PROP_READONLY, 310},
	{   "msg_from_bbsid", BBS_PROP_MSG_FROM_BBSID, PROP_READONLY, 31802},
	{   "msg_from_agent", BBS_PROP_MSG_FROM_AGENT, PROP_READONLY, 310},
	{   "msg_replyto", BBS_PROP_MSG_REPLYTO, PROP_READONLY, 310},
	{   "msg_replyto_ext", BBS_PROP_MSG_REPLYTO_EXT, PROP_READONLY, 310},
	{   "msg_replyto_net", BBS_PROP_MSG_REPLYTO_NET, PROP_READONLY, 310},
	{   "msg_replyto_agent", BBS_PROP_MSG_REPLYTO_AGENT, PROP_READONLY, 310},
	{   "msg_subject", BBS_PROP_MSG_SUBJECT, PROP_READONLY, 310},
	{   "msg_date", BBS_PROP_MSG_DATE, PROP_READONLY, 310},
	{   "msg_timezone", BBS_PROP_MSG_TIMEZONE, PROP_READONLY, 310},
	{   "msg_date_imported", BBS_PROP_MSG_DATE_IMPORTED, PROP_READONLY, 310},
	{   "msg_attr", BBS_PROP_MSG_ATTR, PROP_READONLY, 310},
	{   "msg_auxattr", BBS_PROP_MSG_AUXATTR, PROP_READONLY, 310},
	{   "msg_netattr", BBS_PROP_MSG_NETATTR, PROP_READONLY, 310},
	{   "msg_offset", BBS_PROP_MSG_OFFSET, PROP_READONLY, 310},
	{   "msg_number", BBS_PROP_MSG_NUMBER, JSPROP_ENUMERATE, 310},
	{   "msg_expiration", BBS_PROP_MSG_EXPIRATION, PROP_READONLY, 310},
	{   "msg_forwarded", BBS_PROP_MSG_FORWARDED, PROP_READONLY, 310},
	{   "msg_thread_id", BBS_PROP_MSG_THREAD_BACK, PROP_READONLY, 316},
	{   "msg_thread_back", BBS_PROP_MSG_THREAD_BACK, PROP_READONLY, 312},
	{   "msg_thread_orig", BBS_PROP_MSG_THREAD_BACK, JSPROP_READONLY, 310},     /* alias */
	{   "msg_thread_next", BBS_PROP_MSG_THREAD_NEXT, PROP_READONLY, 310},
	{   "msg_thread_first", BBS_PROP_MSG_THREAD_FIRST, PROP_READONLY, 310},
	{   "msg_id", BBS_PROP_MSG_ID, PROP_READONLY, 310},
	{   "msg_reply_id", BBS_PROP_MSG_REPLY_ID, PROP_READONLY, 310},
	{   "msg_delivery_attempts", BBS_PROP_MSG_DELIVERY_ATTEMPTS
		, PROP_READONLY, 310},

	{   "msghdr_top_of_screen", BBS_PROP_MSGHDR_TOS, PROP_READONLY, 31702},

	{   "file_name", BBS_PROP_FILE_NAME, PROP_READONLY, 317},
	{   "file_description", BBS_PROP_FILE_DESC, PROP_READONLY, 317},
	{   "file_dir_number", BBS_PROP_FILE_DIR, PROP_READONLY, 317},
	{   "file_attr", BBS_PROP_FILE_ATTR, PROP_READONLY, 317},
	{   "file_date", BBS_PROP_FILE_DATE, PROP_READONLY, 317},
	{   "file_size", BBS_PROP_FILE_SIZE, PROP_READONLY, 317},
	{   "file_credits", BBS_PROP_FILE_CREDITS, PROP_READONLY, 317},
	{   "file_uploader", BBS_PROP_FILE_ULER, PROP_READONLY, 317},
	{   "file_upload_date", BBS_PROP_FILE_DATE_ULED, PROP_READONLY, 317},
	{   "file_download_date", BBS_PROP_FILE_DATE_DLED, PROP_READONLY, 317},
	{   "file_download_count", BBS_PROP_FILE_TIMES_DLED, PROP_READONLY, 317},

	{   "download_cps", BBS_PROP_DOWNLOAD_CPS, PROP_READONLY, 320},
	{   "batch_upload_total", BBS_PROP_BATCH_UPLOAD_TOTAL, PROP_READONLY, 310},
	{   "batch_dnload_total", BBS_PROP_BATCH_DNLOAD_TOTAL, PROP_READONLY, 310},

	{   "command_str", BBS_PROP_COMMAND_STR, JSPROP_ENUMERATE, 314},
/* Utility functions */
static uint get_subnum(JSContext* cx, sbbs_t* sbbs, jsval *argv, int argc, int pos)
	int subnum = INVALID_SUB;
	if (argc > pos && JSVAL_IS_STRING(argv[pos])) {
deuce's avatar
deuce committed
		char * p;

		JSSTRING_TO_ASTRING(cx, JSVAL_TO_STRING(argv[pos]), p, LEN_EXTCODE + 2, NULL);
		for (subnum = 0; subnum < sbbs->cfg.total_subs; subnum++)
			if (!stricmp(sbbs->cfg.sub[subnum]->code, p))
	} else if (argc > pos && JSVAL_IS_NUMBER(argv[pos])) {
		if (!JS_ValueToECMAUint32(cx, argv[pos], &i))
	else if (sbbs->usrgrps > 0)
		subnum = sbbs->usrsub[sbbs->curgrp][sbbs->cursub[sbbs->curgrp]];
static uint get_dirnum(JSContext* cx, sbbs_t* sbbs, jsval val, bool dflt)
	int dirnum = INVALID_DIR;
	if (sbbs->usrlibs > 0)
		dirnum = sbbs->usrdir[sbbs->curlib][sbbs->curdir[sbbs->curlib]];
	if (!dflt) {
		if (JSVAL_IS_STRING(val)) {
			char *p;
			JSSTRING_TO_ASTRING(cx, JSVAL_TO_STRING(val), p, LEN_EXTCODE + 2, NULL);
			for (dirnum = 0; dirnum < sbbs->cfg.total_dirs; dirnum++)
				if (!stricmp(sbbs->cfg.dir[dirnum]->code, p))
		} else if (JSVAL_IS_NUMBER(val)) {
			if (!JS_ValueToECMAUint32(cx, val, &i))
		else if (sbbs->usrlibs > 0)
			dirnum = sbbs->usrdir[sbbs->curlib][sbbs->curdir[sbbs->curlib]];
/**************************/
/* bbs Object Methods */
/**************************/

static JSBool
js_menu(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	JSString*  str;
	sbbs_t*    sbbs;
	jsrefcount rc;
	char *     menu;
	int32      mode = P_NONE;
	JSObject*  obj = JS_GetScopeChain(cx);
	if (!js_argc(cx, argc, 1))
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	str = JS_ValueToString(cx, argv[0]);
	if (!str)
		return(JS_FALSE);
	if (argc > argn && JSVAL_IS_NUMBER(argv[argn])) {
		if (!JS_ValueToInt32(cx, argv[argn], &mode))
	if (argc > argn && JSVAL_IS_OBJECT(argv[argn])) {
		if ((obj = JSVAL_TO_OBJECT(argv[argn])) == NULL)
deuce's avatar
deuce committed
	JSSTRING_TO_MSTRING(cx, str, menu, NULL);
deuce's avatar
deuce committed
		return JS_FALSE;
	rc = JS_SUSPENDREQUEST(cx);
	bool result = sbbs->menu(menu, mode, obj);
deuce's avatar
deuce committed
	free(menu);
	JS_SET_RVAL(cx, arglist, result ? JSVAL_TRUE : JSVAL_FALSE);
rswindell's avatar
rswindell committed
static JSBool
js_menu_exists(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *    argv = JS_ARGV(cx, arglist);
	JSString*  str;
	sbbs_t*    sbbs;
	jsrefcount rc;
	char *     menu;
	if (!js_argc(cx, argc, 1))
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	str = JS_ValueToString(cx, argv[0]);
	if (!str)
		return(JS_FALSE);
rswindell's avatar
rswindell committed

	JSSTRING_TO_MSTRING(cx, str, menu, NULL);
rswindell's avatar
rswindell committed
		return JS_FALSE;
	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	bool result = sbbs->menu_exists(menu);
	free(menu);
	JS_RESUMEREQUEST(cx, rc);

	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(result));

rswindell's avatar
rswindell committed
static JSBool
js_hangup(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	sbbs_t*    sbbs;
	jsrefcount rc;
rswindell's avatar
rswindell committed

	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->hangup();
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

rswindell's avatar
rswindell committed
static JSBool
js_nodesync(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval*     argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	jsrefcount rc;
	JSBool     clearline = false;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	if (argc > 0 && JSVAL_IS_BOOLEAN(argv[0]))
		clearline = JSVAL_TO_BOOLEAN(argv[0]);

	rc = JS_SUSPENDREQUEST(cx);
	sbbs->getnodedat(sbbs->cfg.node_num, &sbbs->thisnode);
	sbbs->nodesync(clearline ? true : false);
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

static JSBool
js_exec(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	uintN      i;
	sbbs_t*    sbbs;
	uint32     mode = 0;
	JSString*  cmd;
	JSString*  startup_dir = NULL;
	char*      p_startup_dir = NULL;
	jsrefcount rc;
	char*      cstr;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (!js_argc(cx, argc, 1))
	if ((cmd = JS_ValueToString(cx, argv[0])) == NULL)
	for (i = 1; i < argc; i++) {
		if (JSVAL_IS_NUMBER(argv[i])) {
			if (!JS_ValueToECMAUint32(cx, argv[i], &mode))
		else if (JSVAL_IS_STRING(argv[i]))
			startup_dir = JS_ValueToString(cx, argv[i]);
	if (startup_dir != NULL) {
deuce's avatar
deuce committed
		JSSTRING_TO_MSTRING(cx, startup_dir, p_startup_dir, NULL);
		if (p_startup_dir == NULL)
deuce's avatar
deuce committed
			return JS_FALSE;
	}
deuce's avatar
deuce committed
	JSSTRING_TO_MSTRING(cx, cmd, cstr, NULL);
deuce's avatar
deuce committed
		return JS_FALSE;
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(sbbs->external(cstr, mode, p_startup_dir)));
deuce's avatar
deuce committed
	free(cstr);
deuce's avatar
deuce committed
		free(p_startup_dir);
js_exec_xtrn(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	int32      i = 0;
	char*      code;
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (!js_argc(cx, argc, 1))
	if (JSVAL_IS_STRING(argv[0])) {
		JSVALUE_TO_ASTRING(cx, argv[0], code, LEN_CODE + 2, NULL);
		if (code == NULL)
		for (i = 0; i < sbbs->cfg.total_xtrns; i++)
			if (!stricmp(sbbs->cfg.xtrn[i]->code, code))
	} else if (JSVAL_IS_NUMBER(argv[0])) {
		if (!JS_ValueToInt32(cx, argv[0], &i))
	if (i < 0 || i >= sbbs->cfg.total_xtrns) {
		JS_ReportError(cx, "Invalid external program specified");
		return JS_FALSE;
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->exec_xtrn(i)));
	return(JS_TRUE);
}

static JSBool
js_user_event(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	uint32     i = 0;
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (argc && JSVAL_IS_NUMBER(argv[0])) {
		if (!JS_ValueToECMAUint32(cx, argv[0], &i))
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->user_event((user_event_t)i)));
static JSBool
js_checkfname(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *    argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	char*      fname = NULL;
	jsrefcount rc;
	if (argc < 1 || !JSVAL_IS_STRING(argv[0]))
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
		return JS_FALSE;

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

	return JS_TRUE;
}

static JSBool
js_chksyspass(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	char*      sys_pw = NULL;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
		return(JS_FALSE);

	if (argc) {
		JSString* str = JS_ValueToString(cx, argv[0]);
		JSSTRING_TO_ASTRING(cx, str, sys_pw, sizeof(sbbs->cfg.sys_pass) + 2, NULL);
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->chksyspass(sys_pw)));
	return(JS_TRUE);
}
js_chkpass(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	char*      cstr;
	jsrefcount rc;
	bool       unique = false;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (!js_argc(cx, argc, 1))
	if (argc > 1 && JSVAL_IS_BOOLEAN(argv[1]))
		unique = JSVAL_TO_BOOLEAN(argv[1]);

	JSString* str = JS_ValueToString(cx, argv[0]);
	JSSTRING_TO_ASTRING(cx, str, cstr, LEN_PASS + 2, NULL);
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->chkpass(cstr, &sbbs->useron, unique)));
js_text(JSContext *cx, uintN argc, jsval *arglist)
	jsval * argv = JS_ARGV(cx, arglist);
	uint32  i = 0;
	sbbs_t* sbbs;
	bool    dflt = false;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	JS_SET_RVAL(cx, arglist, JSVAL_NULL);
	if (argc > 0) {
		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 = sbbs->get_text_num(id) + 1;
			free(id);
		}
		if (argc > 1 && JSVAL_IS_BOOLEAN(argv[1]))
Rob Swindell's avatar
Rob Swindell committed
			dflt = JSVAL_TO_BOOLEAN(argv[1]);
	if (i > 0 && i <= TOTAL_TEXT) {
Rob Swindell's avatar
Rob Swindell committed
		JSString* js_str = JS_NewStringCopyZ(cx, dflt ? sbbs->text_sav[i - 1] : sbbs->text[i - 1]);
			return(JS_FALSE);
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
js_replace_text(JSContext *cx, uintN argc, jsval *arglist)
	jsval * argv = JS_ARGV(cx, arglist);
	char*   p;
	uint32  i = 0;
	int     len;
	sbbs_t* sbbs;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (!js_argc(cx, argc, 2))
	if (JSVAL_IS_NUMBER(argv[0])) {
		if (!JS_ValueToECMAUint32(cx, argv[0], &i))
	} else {
		JSString* js_str = JS_ValueToString(cx, argv[0]);
		JSSTRING_TO_MSTRING(cx, js_str, id, NULL);
		i = sbbs->get_text_num(id) + 1;
		free(id);
	if (i < 1 || i > TOTAL_TEXT)
	if (sbbs->text[i] != sbbs->text_sav[i] && sbbs->text[i] != nulstr)
		free(sbbs->text[i]);
deuce's avatar
deuce committed
	JSVALUE_TO_MSTRING(cx, argv[1], p, NULL);
deuce's avatar
deuce committed
		return(JS_FALSE);
	len = strlen(p);
	if (!len) {
		sbbs->text[i] = (char*)nulstr;
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
deuce's avatar
deuce committed
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
js_revert_text(JSContext *cx, uintN argc, jsval *arglist)
	jsval * argv = JS_ARGV(cx, arglist);
	uint32  i = 0;
	sbbs_t* sbbs;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (!js_argc(cx, argc, 1))
	if (JSVAL_IS_NUMBER(argv[0])) {
		if (!JS_ValueToECMAUint32(cx, argv[0], &i))
		i--;
	} else {
		JSString* js_str = JS_ValueToString(cx, argv[0]);
		JSSTRING_TO_MSTRING(cx, js_str, id, NULL);
		i = sbbs->get_text_num(id);
		free(id);
	if (i >= TOTAL_TEXT) {
		for (i = 0; i < TOTAL_TEXT; i++) {
			if (sbbs->text[i] != sbbs->text_sav[i] && sbbs->text[i] != nulstr)
				free(sbbs->text[i]);
			sbbs->text[i] = sbbs->text_sav[i];
		if (sbbs->text[i] != sbbs->text_sav[i] && sbbs->text[i] != nulstr)
			free(sbbs->text[i]);
		sbbs->text[i] = sbbs->text_sav[i];
	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
js_load_text(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	int        i;
	char       path[MAX_PATH + 1];
	FILE*      stream;
	JSString*  js_str;
	sbbs_t*    sbbs;
	char*      cstr;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if (!js_argc(cx, argc, 1))
	if ((js_str = JS_ValueToString(cx, argv[0])) == NULL) {
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
deuce's avatar
deuce committed
	JSSTRING_TO_MSTRING(cx, js_str, cstr, NULL);
deuce's avatar
deuce committed
		return JS_FALSE;
	rc = JS_SUSPENDREQUEST(cx);
	for (i = 0; i < TOTAL_TEXT; i++) {
		if (sbbs->text[i] != sbbs->text_sav[i]) {
			if (sbbs->text[i] != nulstr)
				free(sbbs->text[i]);
			sbbs->text[i] = sbbs->text_sav[i];
	snprintf(path, sizeof path, "%s%s.dat"
	         , sbbs->cfg.ctrl_dir, cstr);
deuce's avatar
deuce committed
	free(cstr);
	if ((stream = fnopen(NULL, path, O_RDONLY)) == NULL) {
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	for (i = 0; i < TOTAL_TEXT && !feof(stream); i++) {
		if ((sbbs->text[i] = readtext(NULL, stream, i)) == NULL) {
		if (!strcmp(sbbs->text[i], sbbs->text_sav[i])) {  /* If identical */
			free(sbbs->text[i]);                    /* Don't alloc */
			sbbs->text[i] = sbbs->text_sav[i];
		else if (sbbs->text[i][0] == 0) {
			free(sbbs->text[i]);
			sbbs->text[i] = (char*)nulstr;
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
Rob Swindell's avatar
Rob Swindell committed
static JSBool
js_load_user_text(JSContext* cx, uintN argc, jsval* arglist)
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	bool       result;
Rob Swindell's avatar
Rob Swindell committed

	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
	result = sbbs->load_user_text();
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(result));
	JS_RESUMEREQUEST(cx, rc);

	return(JS_TRUE);
}

js_atcode(JSContext *cx, uintN argc, jsval *arglist)
	jsval *     argv = JS_ARGV(cx, arglist);
	sbbs_t*     sbbs;
	char        str[128];
	char *      instr;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (!js_argc(cx, argc, 1))
deuce's avatar
deuce committed
	JSVALUE_TO_MSTRING(cx, argv[0], instr, NULL);
	rc = JS_SUSPENDREQUEST(cx);
	cp = sbbs->formatted_atcode(instr, str, sizeof(str));
deuce's avatar
deuce committed
	free(instr);
		JS_SET_RVAL(cx, arglist, JSVAL_NULL);
		JSString* js_str = JS_NewStringCopyZ(cx, cp);
			return(JS_FALSE);
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
static JSBool
js_expand_atcodes(JSContext* cx, uintN argc, jsval* arglist)
{
	jsval*     argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	char       result[256] = "";
	char*      instr;
	jsrefcount rc;

	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
		return JS_FALSE;

	smbmsg_t* msg = (smbmsg_t*)sbbs->current_msg;

	if (!js_argc(cx, argc, 1))
		return JS_FALSE;

	JSVALUE_TO_MSTRING(cx, argv[0], instr, NULL);
	if (instr == NULL)
		return JS_FALSE;

	if (argc > 1 && (JSVAL_IS_OBJECT(argv[1]) && !JSVAL_IS_NULL(argv[1]))) {
		if ((hdrobj = JSVAL_TO_OBJECT(argv[1])) == NULL) {
		if (!js_GetMsgHeaderObjectPrivates(cx, hdrobj, /* smb_t: */ NULL, &msg, /* post: */ NULL)) {
	sbbs->expand_atcodes(instr, result, sizeof result, msg);
	free(instr);
	JS_RESUMEREQUEST(cx, rc);
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, result)));

	return JS_TRUE;
}
rswindell's avatar
rswindell committed
static JSBool
js_logkey(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      p;
	JSBool     comma = false;
	JSString*  js_str;
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	if ((js_str = JS_ValueToString(cx, argv[0])) == NULL) {
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
rswindell's avatar
rswindell committed
		return(JS_TRUE);
	}

	if (argc > 1)
		JS_ValueToBoolean(cx, argv[1], &comma);
deuce's avatar
deuce committed
	JSSTRING_TO_MSTRING(cx, js_str, p, NULL);
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
rswindell's avatar
rswindell committed
		return(JS_TRUE);
	}

	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->logch(*p
	            , comma ? true:false // This is a dumb bool conversion to make BC++ happy
	            );
deuce's avatar
deuce committed
	free(p);
	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
rswindell's avatar
rswindell committed
	return(JS_TRUE);
}

static JSBool
js_logstr(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      p;
	JSString*  js_str;
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if (!js_argc(cx, argc, 1))
	if ((js_str = JS_ValueToString(cx, argv[0])) == NULL) {
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
rswindell's avatar
rswindell committed
		return(JS_TRUE);
	}

deuce's avatar
deuce committed
	JSSTRING_TO_MSTRING(cx, js_str, p, NULL);
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
rswindell's avatar
rswindell committed
		return(JS_TRUE);
	}

	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->log(p);
deuce's avatar
deuce committed
	free(p);
	JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
rswindell's avatar
rswindell committed
	return(JS_TRUE);
}

static JSBool
js_finduser(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      p;
	JSString*  js_str;
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (!js_argc(cx, argc, 1))
	if ((js_str = JS_ValueToString(cx, argv[0])) == NULL) {
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(0));
rswindell's avatar
rswindell committed
		return(JS_TRUE);
	}

deuce's avatar
deuce committed
	JSSTRING_TO_MSTRING(cx, js_str, p, NULL);
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(0));
rswindell's avatar
rswindell committed
		return(JS_TRUE);
	}

	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(sbbs->finduser(p, /* silent_failure: */ true)));
deuce's avatar
deuce committed
	free(p);
rswindell's avatar
rswindell committed
	return(JS_TRUE);
}

static JSBool
js_trashcan(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      str;
	char*      can;
	JSString*  js_str;
	JSString*  js_can;
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (!js_argc(cx, argc, 2))
	if ((js_can = JS_ValueToString(cx, argv[0])) == NULL) {
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
rswindell's avatar
rswindell committed
		return(JS_TRUE);
	}

	if ((js_str = JS_ValueToString(cx, argv[1])) == NULL) {
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
rswindell's avatar
rswindell committed
		return(JS_TRUE);
	}

deuce's avatar
deuce committed
	JSSTRING_TO_MSTRING(cx, js_can, can, NULL);
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
rswindell's avatar
rswindell committed
		return(JS_TRUE);
	}

deuce's avatar
deuce committed
	JSSTRING_TO_MSTRING(cx, js_str, str, NULL);
deuce's avatar
deuce committed
		free(can);
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
rswindell's avatar
rswindell committed
		return(JS_TRUE);
	}

	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->trashcan(str, can)));
deuce's avatar
deuce committed
	free(can);
	free(str);
rswindell's avatar
rswindell committed
	return(JS_TRUE);
}

static JSBool
js_newuser(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->newuser()));
rswindell's avatar
rswindell committed
	return(JS_TRUE);
}

static JSBool
js_logon(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->logon()));
rswindell's avatar
rswindell committed
	return(JS_TRUE);
}

static JSBool
js_login(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      name;
	char*      pw_prompt = NULL;
	char*      user_pw = NULL;
	char*      sys_pw = NULL;
	JSString*  js_name;
	JSString*  js_pw_prompt = NULL;
	JSString*  js_user_pw = NULL;
	JSString*  js_sys_pw = NULL;
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (!js_argc(cx, argc, 2))
	if ((js_name = JS_ValueToString(cx, argv[0])) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

rswindell's avatar
rswindell committed
		js_pw_prompt = JS_ValueToString(cx, argv[1]);
rswindell's avatar
rswindell committed
		js_user_pw = JS_ValueToString(cx, argv[2]);
rswindell's avatar
rswindell committed
		js_sys_pw = JS_ValueToString(cx, argv[3]);
	JSSTRING_TO_ASTRING(cx, js_name, name, (LEN_ALIAS > LEN_NAME) ? LEN_ALIAS + 2 : LEN_NAME + 2, NULL);
	if (name == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	JSSTRING_TO_MSTRING(cx, js_pw_prompt, pw_prompt, NULL);
	JSSTRING_TO_MSTRING(cx, js_user_pw, user_pw, NULL);
	JSSTRING_TO_MSTRING(cx, js_sys_pw, sys_pw, NULL);
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->login(name, pw_prompt, user_pw, sys_pw) == LOGIC_TRUE ? JS_TRUE:JS_FALSE));
	FREE_AND_NULL(pw_prompt);
	FREE_AND_NULL(user_pw);
	FREE_AND_NULL(sys_pw);
rswindell's avatar
rswindell committed
	return(JS_TRUE);
}


static JSBool
js_logoff(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	jsrefcount rc;
	JSBool     prompt = JS_TRUE;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if (argc)
		JS_ValueToBoolean(cx, argv[0], &prompt);
	rc = JS_SUSPENDREQUEST(cx);
	if (!prompt || !sbbs->noyes(sbbs->text[LogOffQ])) {
		if (sbbs->cfg.logoff_mod[0])
			sbbs->exec_bin(sbbs->cfg.logoff_mod, &sbbs->main_csi);
rswindell's avatar
rswindell committed
		sbbs->user_event(EVENT_LOGOFF);
		sbbs->menu("logoff");
		JS_SET_RVAL(cx, arglist, JSVAL_TRUE);
rswindell's avatar
rswindell committed
	}
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

static JSBool
js_logout(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->logout();
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

rswindell's avatar
rswindell committed
static JSBool
js_automsg(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->automsg();
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

js_time_bank(JSContext *cx, uintN argc, jsval *arglist)
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
static JSBool
js_text_sec(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->text_sec();
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

static JSBool
js_chat_sec(JSContext *cx, uintN argc, jsval *arglist)
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
		return(JS_FALSE);

	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	rc = JS_SUSPENDREQUEST(cx);
	sbbs->chatsection();
	JS_RESUMEREQUEST(cx, rc);

	return(JS_TRUE);
}

js_qwk_sec(JSContext *cx, uintN argc, jsval *arglist)
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
static JSBool
js_xtrn_sec(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      section = (char*)"";
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	JS_SET_RVAL(cx, arglist, JSVAL_VOID);
		JSVALUE_TO_ASTRING(cx, argv[0], section, LEN_CODE + 1, NULL);
	}
	rc = JS_SUSPENDREQUEST(cx);
	sbbs->xtrn_sec(section);
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

rswindell's avatar
rswindell committed
static JSBool
js_xfer_policy(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);
	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->xfer_policy();
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}
js_batchmenu(JSContext *cx, uintN argc, jsval *arglist)
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	rc = JS_SUSPENDREQUEST(cx);
js_batchdownload(JSContext *cx, uintN argc, jsval *arglist)
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->start_batch_download()));
js_batchaddlist(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	jsrefcount rc;
	char*      cstr;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if (!js_argc(cx, argc, 1))
deuce's avatar
deuce committed
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
		return JS_FALSE;
	rc = JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
	sbbs->batch_add_list(cstr);
deuce's avatar
deuce committed
	free(cstr);
static JSBool
js_batch_clear(JSContext *cx, uintN argc, jsval *arglist)
	jsval *        argv = JS_ARGV(cx, arglist);
	sbbs_t*        sbbs;
	jsrefcount     rc;
	enum XFER_TYPE xfer_type = XFER_BATCH_DOWNLOAD;

	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
		return(JS_FALSE);

	if (argc > 0 && argv[0] == JSVAL_TRUE)
		xfer_type = XFER_BATCH_UPLOAD;

	rc = JS_SUSPENDREQUEST(cx);
	bool result = batch_list_clear(&sbbs->cfg, sbbs->useron.number, xfer_type);
	JS_RESUMEREQUEST(cx, rc);

	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(result));

	return(JS_TRUE);
}

static JSBool
js_batch_remove(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *        argv = JS_ARGV(cx, arglist);
	sbbs_t*        sbbs;
	jsrefcount     rc;
	enum XFER_TYPE xfer_type = XFER_BATCH_DOWNLOAD;

	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (argc > 0 && argv[0] == JSVAL_TRUE)
		xfer_type = XFER_BATCH_UPLOAD;

	rc = JS_SUSPENDREQUEST(cx);
	if (argc > 1) {
		if (JSVAL_IS_STRING(argv[1])) {
			char* cstr{};
			JSVALUE_TO_MSTRING(cx, argv[1], cstr, NULL);
			result = batch_file_remove(&sbbs->cfg, sbbs->useron.number, xfer_type, cstr);
			free(cstr);
		} else if (JSVAL_IS_NUMBER(argv[1])) {
			result = batch_file_remove_n(&sbbs->cfg, sbbs->useron.number, xfer_type, JSVAL_TO_INT(argv[1]));
		}
	}
	JS_RESUMEREQUEST(cx, rc);

	JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(result));

	return(JS_TRUE);
}

static JSBool
js_batch_sort(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *        argv = JS_ARGV(cx, arglist);
	sbbs_t*        sbbs;
	jsrefcount     rc;
	enum XFER_TYPE xfer_type = XFER_BATCH_DOWNLOAD;

	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (argc > 0 && argv[0] == JSVAL_TRUE)
		xfer_type = XFER_BATCH_UPLOAD;

	rc = JS_SUSPENDREQUEST(cx);
	bool result = batch_list_sort(&sbbs->cfg, sbbs->useron.number, xfer_type);
	JS_RESUMEREQUEST(cx, rc);

	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(result));

	return(JS_TRUE);
}

static JSBool
js_xfer_prot_menu(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *        argv = JS_ARGV(cx, arglist);
	sbbs_t*        sbbs;
	char           keys[128];
	jsrefcount     rc;
	enum XFER_TYPE xfer_type = XFER_DOWNLOAD;

	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
		return(JS_FALSE);

	if (argc > 0 && argv[0] == JSVAL_TRUE)
		xfer_type = XFER_UPLOAD;
	if (argc > 1 && argv[1] == JSVAL_TRUE)
		xfer_type = ((xfer_type == XFER_UPLOAD) ? XFER_BATCH_UPLOAD : XFER_BATCH_DOWNLOAD);
	rc = JS_SUSPENDREQUEST(cx);
	sbbs->xfer_prot_menu(xfer_type, &sbbs->useron, keys, sizeof keys);
	JSString* js_str = JS_NewStringCopyZ(cx, keys);
	if (js_str == nullptr)
		return JS_FALSE;
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
	JS_RESUMEREQUEST(cx, rc);

static JSBool
js_viewfile(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *    argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	jsrefcount rc;
	char*      cstr;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
		return JS_FALSE;

	if (!js_argc(cx, argc, 1))
		return JS_FALSE;

	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
		return JS_FALSE;

	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->viewfile(cstr)));
	free(cstr);
	JS_RESUMEREQUEST(cx, rc);

	return JS_TRUE;
}

js_sendfile(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	char       prot = 0;
	char*      desc = NULL;
	bool       autohang = true;
	char*      p;
	jsrefcount rc;
	char*      cstr;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (!js_argc(cx, argc, 1))
		JSVALUE_TO_ASTRING(cx, argv[1], p, 8, NULL);
		if (p != NULL)
			prot = *p;
		if (argc > argn && JSVAL_IS_STRING(argv[argn])) {
			JSVALUE_TO_MSTRING(cx, argv[argn], desc, NULL);
			argn++;
		}
		if (argc > argn && JSVAL_IS_BOOLEAN(argv[argn])) {
			autohang = JSVAL_TO_BOOLEAN(argv[argn]);
			argn++;
		}
deuce's avatar
deuce committed
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
		free(cstr);
		free(desc);
deuce's avatar
deuce committed
		return JS_FALSE;
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->sendfile(cstr, prot, desc, autohang)));
deuce's avatar
deuce committed
	free(cstr);
	free(desc);
js_recvfile(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	char       prot = 0;
	bool       autohang = true;
	char*      p;
	char*      cstr;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (!js_argc(cx, argc, 1))
		JSVALUE_TO_ASTRING(cx, argv[1], p, 8, NULL);
		if (p != NULL)
			prot = *p;
		if (argc > 2)
			autohang = JSVAL_TO_BOOLEAN(argv[2]);
deuce's avatar
deuce committed
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
		return JS_FALSE;
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->recvfile(cstr, prot, autohang)));
deuce's avatar
deuce committed
	free(cstr);
js_temp_xfer(JSContext *cx, uintN argc, jsval *arglist)
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
static JSBool
js_user_config(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->maindflts(&sbbs->useron);
	if (!(sbbs->useron.rest & FLAG('G')))    /* not guest */
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

js_user_sync(JSContext *cx, uintN argc, jsval *arglist)
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
static JSBool
js_sys_info(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->sys_info();
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

static JSBool
js_sub_info(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	int subnum = get_subnum(cx, sbbs, argv, argc, 0);
	rc = JS_SUSPENDREQUEST(cx);
	if (sbbs->subnum_is_valid(subnum))
		sbbs->subinfo(subnum);
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

static JSBool
js_dir_info(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	int dirnum = get_dirnum(cx, sbbs, argv[0], argc == 0);
	rc = JS_SUSPENDREQUEST(cx);
	if (sbbs->dirnum_is_valid(dirnum))
		sbbs->dirinfo(dirnum);
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

static JSBool
js_user_info(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->user_info();
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

static JSBool
js_ver(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->ver();
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

static JSBool
js_sys_stats(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->sys_stats();
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

static JSBool
js_node_stats(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	uint32     node_num = 0;
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	if (argc > 0 && JSVAL_IS_NUMBER(argv[0])) {
		if (!JS_ValueToECMAUint32(cx, argv[0], &node_num))
	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->node_stats(node_num);
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

static JSBool
js_userlist(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	uint32     mode = UL_ALL;
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	if (argc > 0 && JSVAL_IS_NUMBER(argv[0])) {
		if (!JS_ValueToECMAUint32(cx, argv[0], &mode))
	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->userlist(mode);
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

static JSBool
js_useredit(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	int32      usernumber = 0;
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	if (argc > 0 && JSVAL_IS_NUMBER(argv[0])) {
		if (!JS_ValueToInt32(cx, argv[0], &usernumber))
	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->useredit(usernumber);
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

js_change_user(JSContext *cx, uintN argc, jsval *arglist)
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
static JSBool
js_logonlist(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	char*      args = (char*)"";
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

		JSVALUE_TO_ASTRING(cx, argv[0], args, LEN_CMD, NULL);
	}
	rc = JS_SUSPENDREQUEST(cx);
	sbbs->logonlist(args);
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

static JSBool
js_nodelist(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->nodelist();
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

static JSBool
js_whos_online(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->whos_online(true);
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

static JSBool
js_spy(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	int32      node_num = 0;
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if (!js_argc(cx, argc, 1))
	if (JSVAL_IS_NUMBER(argv[0])) {
		if (!JS_ValueToInt32(cx, argv[0], &node_num))
	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

js_readmail(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	uint32     readwhich = MAIL_YOUR;
	uint32     usernumber;
	uint32     lm_mode = 0;
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	usernumber = sbbs->useron.number;
	if (argc > 0 && JSVAL_IS_NUMBER(argv[0])) {
		if (!JS_ValueToECMAUint32(cx, argv[0], &readwhich))
	if (argc > 1 && JSVAL_IS_NUMBER(argv[1])) {
		if (!JS_ValueToECMAUint32(cx, argv[1], &usernumber))
	if (argc > 2 && JSVAL_IS_NUMBER(argv[2])) {
		if (!JS_ValueToECMAUint32(cx, argv[2], &lm_mode))
rswindell's avatar
rswindell committed
			return JS_FALSE;
	}
	rc = JS_SUSPENDREQUEST(cx);
	int result = sbbs->readmail(usernumber, readwhich, lm_mode);
	JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(result));

js_email(JSContext *cx, uintN argc, jsval *arglist)
	jsval *     argv = JS_ARGV(cx, arglist);
	uint32      usernumber = 1;
	uint32      mode = WM_EMAIL;
	const char *def = "";
	char*       top = (char *)def;
	char*       subj = (char *)def;
	JSString*   js_top = NULL;
	JSString*   js_subj = NULL;
	JSObject*   hdrobj;
	sbbs_t*     sbbs;
	smb_t*      resmb = NULL;
	smbmsg_t*   remsg = NULL;
	smbmsg_t    msg;
	jsrefcount  rc;

	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (!js_argc(cx, argc, 1))
	if (JSVAL_IS_NUMBER(argv[0])) {
		if (!JS_ValueToECMAUint32(cx, argv[0], &usernumber))
	for (uintN i = 1; i < argc; i++) {
		if (JSVAL_IS_NUMBER(argv[i])) {
			if (!JS_ValueToECMAUint32(cx, argv[i], &mode))
		else if (JSVAL_IS_STRING(argv[i]) && js_top == NULL)
			js_top = JS_ValueToString(cx, argv[i]);
		else if (JSVAL_IS_STRING(argv[i]))
			js_subj = JS_ValueToString(cx, argv[i]);
		else if (JSVAL_IS_OBJECT(argv[i]) && !JSVAL_IS_NULL(argv[i])) {
			if ((hdrobj = JSVAL_TO_OBJECT(argv[i])) == NULL)
			if (!js_GetMsgHeaderObjectPrivates(cx, hdrobj, &resmb, &remsg, /* post: */ NULL)) {
				if (!js_ParseMsgHeaderObject(cx, hdrobj, &msg)) {
					JS_ReportError(cx, "msg hdr object cannot be parsed");
deuce's avatar
deuce committed
		JSSTRING_TO_MSTRING(cx, js_top, top, NULL);
deuce's avatar
deuce committed
		return JS_FALSE;
	if (js_subj != NULL)
deuce's avatar
deuce committed
		JSSTRING_TO_MSTRING(cx, js_subj, subj, NULL);
	if (subj == NULL) {
		if (top != def)
deuce's avatar
deuce committed
		return JS_FALSE;
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->email(usernumber, top, subj, mode, resmb, remsg)));
	smb_freemsgmem(&msg);
deuce's avatar
deuce committed
		free(top);
deuce's avatar
deuce committed
		free(subj);
js_netmail(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	uint32     mode = 0;
	char*      to = NULL;
	char*      subj = NULL;
	JSString*  js_str;
	JSObject*  hdrobj;
	sbbs_t*    sbbs;
	smb_t*     resmb = NULL;
	smbmsg_t*  remsg = NULL;
	str_list_t to_list = NULL;
	smbmsg_t   msg;
	jsrefcount rc;
	bool       error = false;

	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	if (!js_argc(cx, argc, 1))
	for (uintN i = 0; i < argc && !error; i++) {
		if (JSVAL_IS_NUMBER(argv[i])) {
			if (!JS_ValueToECMAUint32(cx, argv[i], &mode))
				error = true;
		else if (JSVAL_IS_STRING(argv[i])) {
			if ((js_str = JS_ValueToString(cx, argv[i])) == NULL) {
				error = true;
				break;
			}
			if (to == NULL && to_list == NULL) {
rswindell's avatar
rswindell committed
				JSSTRING_TO_MSTRING(cx, js_str, to, NULL);
			} else if (subj == NULL) {
rswindell's avatar
rswindell committed
				JSSTRING_TO_MSTRING(cx, js_str, subj, NULL);
			}
		}
		else if (JSVAL_IS_OBJECT(argv[i]) && !JSVAL_IS_NULL(argv[i])) {
			if ((hdrobj = JSVAL_TO_OBJECT(argv[i])) == NULL) {
				error = true;
				break;
			}
			jsuint len = 0;
			if (JS_GetArrayLength(cx, hdrobj, &len) && len > 0) { // to_list[]
				to_list = strListInit();
				for (jsuint j = 0; j < len; j++) {
					jsval val;
					if (!JS_GetElement(cx, hdrobj, j, &val)) {
						error = true;
						break;
					}
					if ((js_str = JS_ValueToString(cx, val)) == NULL) {
						error = true;
						break;
					}
					char* cstr = NULL;
					JSSTRING_TO_ASTRING(cx, js_str, cstr, 64, NULL);
						error = true;
						break;
					}
					strListPush(&to_list, cstr);
				}
				continue;
			}
			if (!js_GetMsgHeaderObjectPrivates(cx, hdrobj, &resmb, &remsg, /* post: */ NULL)) {
				if (!js_ParseMsgHeaderObject(cx, hdrobj, &msg)) {
					JS_ReportError(cx, "msg hdr object cannot be parsed");
					error = true;
					break;
	rc = JS_SUSPENDREQUEST(cx);
	if (!error)
		JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->netmail(to, subj, mode, resmb, remsg, to_list)));
	strListFree(&to_list);
rswindell's avatar
rswindell committed
	FREE_AND_NULL(subj);
	FREE_AND_NULL(to);
js_bulkmail(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	uchar*     ar = NULL;
	sbbs_t*    sbbs;
	jsrefcount rc;
	char *     p;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
deuce's avatar
deuce committed
		JSVALUE_TO_MSTRING(cx, argv[0], p, NULL);
		ar = arstr(NULL, p, &sbbs->cfg, NULL);
deuce's avatar
deuce committed
		free(p);
	rc = JS_SUSPENDREQUEST(cx);
	sbbs->bulkmail(ar);
js_upload_file(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	uint       dirnum = 0;
	sbbs_t*    sbbs;
	jsrefcount rc;
	char*      fname = nullptr;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	dirnum = get_dirnum(cx, sbbs, argv[0], argc == 0);
	if (!dirnum_is_valid(&sbbs->cfg, dirnum)) {
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
		return(JS_TRUE);
	}

	if (argc > 1 && JSVAL_IS_STRING(argv[1])) {
		JSString* js_str;
		if ((js_str = JS_ValueToString(cx, argv[1])) == NULL)
			return JS_FALSE;
		JSSTRING_TO_MSTRING(cx, js_str, fname, NULL);
	}

	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->upload(dirnum, fname)));
	free(fname);
	return(JS_TRUE);
}

static JSBool
js_batch_upload(JSContext *cx, uintN argc, jsval *arglist)
{
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->batch_upload()));
	JS_RESUMEREQUEST(cx, rc);
	return JS_TRUE;
}

static JSBool
js_bulkupload(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	uint       dirnum = 0;
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	dirnum = get_dirnum(cx, sbbs, argv[0], argc == 0);
	if (!dirnum_is_valid(&sbbs->cfg, dirnum)) {
		JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->bulkupload(dirnum) == 0));
rswindell's avatar
rswindell committed
static JSBool
js_telnet_gate(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *    argv = JS_ARGV(cx, arglist);
	uintN      argn;
	char*      addr;
	uint32     mode = 0;
	uint32     timeout = 10;
	JSString*  js_addr;
	sbbs_t*    sbbs;
	str_list_t send_strings = NULL;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (!js_argc(cx, argc, 1))
	if ((js_addr = JS_ValueToString(cx, argv[0])) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

deuce's avatar
deuce committed
	JSSTRING_TO_MSTRING(cx, js_addr, addr, NULL);
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	if (argc > argn && JSVAL_IS_NUMBER(argv[argn])) {
		if (!JS_ValueToECMAUint32(cx, argv[argn], &mode)) {
deuce's avatar
deuce committed
			free(addr);
	if (argc > argn && JSVAL_IS_NUMBER(argv[argn])) {
		if (!JS_ValueToECMAUint32(cx, argv[argn], &timeout)) {
			free(addr);
			return JS_FALSE;
		}
	if (argc > argn && JSVAL_IS_OBJECT(argv[argn])) {
		JSObject* array = JSVAL_TO_OBJECT(argv[argn]);
		jsuint    count = 0;
		if (array != NULL && JS_IsArrayObject(cx, array) && JS_GetArrayLength(cx, array, &count)) {
			for (jsuint i = 0; i < count; ++i) {
				if (!JS_GetElement(cx, array, i, &val))
					break;
				JSVALUE_TO_RASTRING(cx, val, tmp, &tmplen, NULL);
				strListPush(&send_strings, tmp);
			}
			free(tmp);
			++argn;
		}
	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->telnet_gate(addr, mode, timeout, send_strings)));
deuce's avatar
deuce committed
	free(addr);
rswindell's avatar
rswindell committed
	return(JS_TRUE);
}

#define TG_MODE_UNSPECIFIED ~0U
static JSBool
js_rlogin_gate(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval*     argv = JS_ARGV(cx, arglist);
	uintN      argn;
	char*      addr;
	char*      client_user_name = NULL;
	char*      server_user_name = NULL;
	char*      term_type = NULL;
	bool       fail = false;
	uint32     mode = TG_MODE_UNSPECIFIED;
	uint32     timeout = 10;
	JSString*  js_str;
	sbbs_t*    sbbs;
	str_list_t send_strings = NULL;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
	if (!js_argc(cx, argc, 1))
	if ((js_str = JS_ValueToString(cx, argv[0])) == NULL)
	JSSTRING_TO_MSTRING(cx, js_str, addr, NULL);
	/* Parse optional arguments if provided */
	for (argn = 1; argn < argc; argn++) {
		if (JSVAL_IS_STRING(argv[argn])) {
			if ((js_str = JS_ValueToString(cx, argv[argn])) == NULL) {
			if (client_user_name == NULL) {
				JSSTRING_TO_MSTRING(cx, js_str, client_user_name, NULL);
			} else if (server_user_name == NULL) {
				JSSTRING_TO_MSTRING(cx, js_str, server_user_name, NULL);
			} else if (term_type == NULL) {
				JSSTRING_TO_MSTRING(cx, js_str, term_type, NULL);
			}
		} else if (JSVAL_IS_NUMBER(argv[argn])) {
			if (mode == TG_MODE_UNSPECIFIED) {
				if (!JS_ValueToECMAUint32(cx, argv[argn], &mode)) {
				if (!JS_ValueToECMAUint32(cx, argv[argn], &timeout)) {
		} else if (JSVAL_IS_OBJECT(argv[argn])) {
			JSObject* array = JSVAL_TO_OBJECT(argv[argn]);
			jsuint    count = 0;
			if (array != NULL && JS_IsArrayObject(cx, array) && JS_GetArrayLength(cx, array, &count)) {
				for (jsuint i = 0; i < count; ++i) {
					if (!JS_GetElement(cx, array, i, &val))
						break;
					JSVALUE_TO_RASTRING(cx, val, tmp, &tmplen, NULL);
					strListPush(&send_strings, tmp);
				}
				free(tmp);
			}
	if (!fail) {
		if (mode == TG_MODE_UNSPECIFIED)
		rc = JS_SUSPENDREQUEST(cx);
		            , BOOLEAN_TO_JSVAL(sbbs->telnet_gate(addr, mode | TG_RLOGIN, timeout, send_strings, client_user_name, server_user_name, term_type)));
	FREE_AND_NULL(client_user_name);
	FREE_AND_NULL(server_user_name);
	FREE_AND_NULL(term_type);
js_pagesysop(JSContext *cx, uintN argc, jsval *arglist)
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->sysop_page()));
rswindell's avatar
rswindell committed
	return(JS_TRUE);
}

js_pageguru(JSContext *cx, uintN argc, jsval *arglist)
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->guru_page()));
rswindell's avatar
rswindell committed
	return(JS_TRUE);
}

js_multinode_chat(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	int32      channel = 1;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	if (argc > 1 && JSVAL_IS_NUMBER(argv[1])) {
		if (!JS_ValueToInt32(cx, argv[1], &channel))
	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->multinodechat(channel);
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

js_private_message(JSContext *cx, uintN argc, jsval *arglist)
	sbbs_t*    sbbs;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	rc = JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
	sbbs->nodemsg();
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

js_private_chat(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	JSBool     local = false;
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	if (argc)
		JS_ValueToBoolean(cx, argv[0], &local);
	rc = JS_SUSPENDREQUEST(cx);
	sbbs->privchat(local ? true:false); // <- eliminates stupid msvc6 "performance warning"
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

js_get_node_message(JSContext *cx, uintN argc, jsval *arglist)
	jsval*     argv = JS_ARGV(cx, arglist);
	sbbs_t*    sbbs;
	jsrefcount rc;
	JSBool     clearline = false;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	if (argc > 0 && JSVAL_IS_BOOLEAN(argv[0]))
		clearline = JSVAL_TO_BOOLEAN(argv[0]);

	rc = JS_SUSPENDREQUEST(cx);
	sbbs->getnmsg(clearline ? true : false);
rswindell's avatar
rswindell committed

	return(JS_TRUE);
}

js_put_node_message(JSContext *cx, uintN argc, jsval *arglist)
	jsval *    argv = JS_ARGV(cx, arglist);
	uintN      argn = 0;
	sbbs_t*    sbbs;
	int32      nodenum = 0;
	JSString*  js_msg;
	char*      msg = NULL;
	char       str[256];
	char       tmp[512];
	jsrefcount rc;
	if ((sbbs = js_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist))) == NULL)
rswindell's avatar
rswindell committed
		return(JS_FALSE);

	/* Get the destination node number */
	if (argn < argc && JSVAL_IS_NUMBER(argv[argn])) {
		if (!JS_ValueToInt32(cx, argv[argn], &nodenum))
		rc = JS_SUSPENDREQUEST(cx);
		nodenum = sbbs->getnodetopage(/* all: */ TRUE, /* telegram: */ FALSE);
		JS_RESUMEREQUEST(cx, rc);
		return JS_TRUE;
	node_t node{};
	if (nodenum >= 1) {  /* !all */
		usernumber = node.useron;
		if ((node.misc & NODE_POFF) && !user_is_sysop(&sbbs->useron)) {
			sbbs->bprintf(sbbs->text[CantPageNode]
			              , node.misc & NODE_ANON ? sbbs->text[UNKNOWN_USER] : username(&sbbs->cfg, node.useron, tmp));
	/* Get the node message text */
	if (argn < argc) {
		if ((js_msg = JS_ValueToString(cx, argv[argn])) == NULL)
			return JS_FALSE;
		argn++;
		JSSTRING_TO_MSTRING(cx, js_msg, msg, NULL);
	} else {
			sbbs->bprintf(sbbs->text[SendingMessageToUser]
			              , node.misc & NODE_ANON ? sbbs->text[UNKNOWN_USER]
			    : username(&sbbs->cfg, node.useron, tmp)
			              , node.misc & NODE_ANON ? 0 : node.useron);
		sbbs->bputs(sbbs->text[NodeMsgPrompt]);
		rc = JS_SUSPENDREQUEST(cx);
		char line[128];
		int  result = sbbs->getstr(line, 69, K_LINE);
		JS_RESUMEREQUEST(cx, rc);
			return JS_TRUE;
		snprintf(str, sizeof str, sbbs->text[nodenum >= 1 ? NodeMsgFmt : AllNodeMsgFmt]
		         , sbbs->cfg.node_num
		         , sbbs->thisnode.misc & NODE_ANON
		        ? sbbs->text[UNKNOWN_USER] : sbbs->useron.alias, line);
		return JS_FALSE;

	/* Send the message(s) */
	rc = JS_SUSPENDREQUEST(cx);
	if (nodenum < 0) {  /* ALL */
		for (int i = 1; i <= sbbs->cfg.sys_nodes && success; i++) {
			if (i == sbbs->cfg.node_num)
			if ((node.status == NODE_INUSE
			     || (user_is_sysop(&sbbs->useron) && node.status == NODE_QUIET))
			    && (user_is_sysop(&sbbs->useron) || !(node.misc & NODE_POFF)))
				if (putnmsg(&sbbs->cfg, i, msg) != 0)
Loading
Loading full blame...