Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

js_console.cpp 53.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/* js_console.cpp */

/* Synchronet JavaScript "Console" Object */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
rswindell's avatar
rswindell committed
11
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
 *																			*
 * 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										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
39
#include "js_request.h"
40 41 42 43 44 45 46

#ifdef JAVASCRIPT

/*****************************/
/* Console Object Properites */
/*****************************/
enum {
47
	 CON_PROP_STATUS
48
	,CON_PROP_LNCNTR
49
	,CON_PROP_COLUMN
50
	,CON_PROP_LASTLINELEN
rswindell's avatar
rswindell committed
51
	,CON_PROP_ATTR
52 53
	,CON_PROP_TOS
	,CON_PROP_ROWS
rswindell's avatar
rswindell committed
54
	,CON_PROP_COLUMNS
rswindell's avatar
rswindell committed
55
	,CON_PROP_TABSTOP
56
	,CON_PROP_AUTOTERM
rswindell's avatar
rswindell committed
57
	,CON_PROP_TERMINAL
58
	,CON_PROP_CTERM_VERSION
rswindell's avatar
rswindell committed
59
	,CON_PROP_WORDWRAP
60
	,CON_PROP_QUESTION
61 62
	,CON_PROP_INACTIV_WARN
	,CON_PROP_INACTIV_HANGUP
63
	,CON_PROP_TIMEOUT			/* User inactivity timeout reference */
64
	,CON_PROP_TIMELEFT_WARN		/* low timeleft warning counter */
65
	,CON_PROP_ABORTED
rswindell's avatar
rswindell committed
66 67
	,CON_PROP_ABORTABLE
	,CON_PROP_TELNET_MODE
68
	,CON_PROP_GETSTR_OFFSET
69
	,CON_PROP_CTRLKEY_PASSTHRU
70 71 72 73 74
	/* read only */
	,CON_PROP_INBUF_LEVEL
	,CON_PROP_INBUF_SPACE
	,CON_PROP_OUTBUF_LEVEL
	,CON_PROP_OUTBUF_SPACE
rswindell's avatar
rswindell committed
75 76
};

77
static JSBool js_console_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
78
{
79 80
	jsval		idval;
	int32		val;
81
    jsint       tiny;
82
	JSString*	js_str;
83 84
	sbbs_t*		sbbs;

85
	if((sbbs=(sbbs_t*)JS_GetPrivate(cx, obj))==NULL)
86 87
		return(JS_FALSE);

88 89
    JS_IdToValue(cx, id, &idval);
    tiny = JSVAL_TO_INT(idval);
90 91 92 93 94 95 96 97

	switch(tiny) {
		case CON_PROP_STATUS:
			val=sbbs->console;
			break;
		case CON_PROP_LNCNTR:
			val=sbbs->lncntr;
			break;
98 99 100
		case CON_PROP_COLUMN:
			val=sbbs->column;
			break;
101 102 103
		case CON_PROP_LASTLINELEN:
			val=sbbs->lastlinelen;
			break;
rswindell's avatar
rswindell committed
104 105 106
		case CON_PROP_ATTR:
			val=sbbs->curatr;
			break;
107 108 109 110 111 112
		case CON_PROP_TOS:
			val=sbbs->tos;
			break;
		case CON_PROP_ROWS:
			val=sbbs->rows;
			break;
rswindell's avatar
rswindell committed
113 114 115
		case CON_PROP_COLUMNS:
			val=sbbs->cols;
			break;
rswindell's avatar
rswindell committed
116 117 118
		case CON_PROP_TABSTOP:
			val=sbbs->tabstop;
			break;
119 120 121
		case CON_PROP_AUTOTERM:
			val=sbbs->autoterm;
			break;
rswindell's avatar
rswindell committed
122 123 124 125 126
		case CON_PROP_TERMINAL:
			if((js_str=JS_NewStringCopyZ(cx, sbbs->terminal))==NULL)
				return(JS_FALSE);
			*vp = STRING_TO_JSVAL(js_str);
			return(JS_TRUE);
127 128 129
		case CON_PROP_CTERM_VERSION:
			val=sbbs->cterm_version;
			break;
130 131 132 133 134 135 136

		case CON_PROP_INACTIV_WARN:
			val=sbbs->cfg.sec_warn;
			break;
		case CON_PROP_INACTIV_HANGUP:
			val=sbbs->cfg.sec_hangup;
			break;
137
		case CON_PROP_TIMEOUT:
138
			val=(int32)sbbs->timeout;
139 140 141 142
			break;
		case CON_PROP_TIMELEFT_WARN:
			val=sbbs->timeleft_warn;
			break;
143
		case CON_PROP_ABORTED:
144 145
			*vp=BOOLEAN_TO_JSVAL(INT_TO_BOOL(sbbs->sys_status&SS_ABORT));
			return(JS_TRUE);
rswindell's avatar
rswindell committed
146
		case CON_PROP_ABORTABLE:
147 148
			*vp=BOOLEAN_TO_JSVAL(INT_TO_BOOL(sbbs->rio_abortable));
			return(JS_TRUE);
rswindell's avatar
rswindell committed
149 150 151
		case CON_PROP_TELNET_MODE:
			val=sbbs->telnet_mode;
			break;
152 153 154
		case CON_PROP_GETSTR_OFFSET:
			val=sbbs->getstr_offset;
			break;
rswindell's avatar
rswindell committed
155
		case CON_PROP_WORDWRAP:
156 157 158
			if((js_str=JS_NewStringCopyZ(cx, sbbs->wordwrap))==NULL)
				return(JS_FALSE);
			*vp = STRING_TO_JSVAL(js_str);
rswindell's avatar
rswindell committed
159
			return(JS_TRUE);
160
		case CON_PROP_QUESTION:
161 162 163
			if((js_str=JS_NewStringCopyZ(cx, sbbs->question))==NULL)
				return(JS_FALSE);
			*vp = STRING_TO_JSVAL(js_str);
164
			return(JS_TRUE);
165 166 167
		case CON_PROP_CTRLKEY_PASSTHRU:
			val=sbbs->cfg.ctrlkey_passthru;
			break;
168 169 170 171 172 173 174 175 176 177 178 179
		case CON_PROP_INBUF_LEVEL:
			val=RingBufFull(&sbbs->inbuf);
			break;
		case CON_PROP_INBUF_SPACE:
			val=RingBufFree(&sbbs->inbuf);
			break;
		case CON_PROP_OUTBUF_LEVEL:
			val=RingBufFull(&sbbs->outbuf);
			break;
		case CON_PROP_OUTBUF_SPACE:
			val=RingBufFree(&sbbs->outbuf);
			break;
180

181 182 183 184 185 186 187 188 189
		default:
			return(JS_TRUE);
	}

	*vp = INT_TO_JSVAL(val);

	return(JS_TRUE);
}

190
static JSBool js_console_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
191
{
192
	jsval idval;
193
	int32		val=0;
194 195
    jsint       tiny;
	sbbs_t*		sbbs;
196
	JSString*	str;
deuce's avatar
deuce committed
197
	jsrefcount	rc;
deuce's avatar
deuce committed
198
	char		*sval;
199

200
	if((sbbs=(sbbs_t*)JS_GetPrivate(cx, obj))==NULL)
201 202
		return(JS_FALSE);

203 204
    JS_IdToValue(cx, id, &idval);
    tiny = JSVAL_TO_INT(idval);
205

206 207 208 209
	if(JSVAL_IS_NUMBER(*vp) || JSVAL_IS_BOOLEAN(*vp)) {
		if(!JS_ValueToInt32(cx, *vp, &val))
			return JS_FALSE;
	}
210 211 212 213 214 215 216 217

	switch(tiny) {
		case CON_PROP_STATUS:
			sbbs->console=val;
			break;
		case CON_PROP_LNCNTR:
			sbbs->lncntr=val;
			break;
218 219 220
		case CON_PROP_COLUMN:
			sbbs->column=val;
			break;
221 222 223
		case CON_PROP_LASTLINELEN:
			sbbs->lastlinelen=val;
			break;
rswindell's avatar
rswindell committed
224
		case CON_PROP_ATTR:
225
			if(JSVAL_IS_STRING(*vp)) {
deuce's avatar
deuce committed
226
				JSVALUE_TO_MSTRING(cx, *vp, sval, NULL);
deuce's avatar
deuce committed
227
				if(sval==NULL)
228
					break;
deuce's avatar
deuce committed
229
				val=attrstr(sval);
deuce's avatar
deuce committed
230
				free(sval);
231
			}
232
			rc=JS_SUSPENDREQUEST(cx);
rswindell's avatar
rswindell committed
233
			sbbs->attr(val);
234
			JS_RESUMEREQUEST(cx, rc);
rswindell's avatar
rswindell committed
235
			break;
236 237 238 239
		case CON_PROP_TOS:
			sbbs->tos=val;
			break;
		case CON_PROP_ROWS:
rswindell's avatar
rswindell committed
240 241
			if(val >= TERM_ROWS_MIN && val <= TERM_ROWS_MAX)
				sbbs->rows=val;
242
			break;
rswindell's avatar
rswindell committed
243
		case CON_PROP_COLUMNS:
rswindell's avatar
rswindell committed
244 245 246 247 248
			if(val >= TERM_COLS_MIN && val <= TERM_COLS_MAX)
				sbbs->cols=val;
			break;
		case CON_PROP_TABSTOP:
			sbbs->tabstop=val;
rswindell's avatar
rswindell committed
249
			break;
250 251 252
		case CON_PROP_AUTOTERM:
			sbbs->autoterm=val;
			break;
rswindell's avatar
rswindell committed
253
		case CON_PROP_TERMINAL:
deuce's avatar
deuce committed
254
			JSVALUE_TO_MSTRING(cx, *vp, sval, NULL);
deuce's avatar
deuce committed
255
			if(sval==NULL)
rswindell's avatar
rswindell committed
256
				break;
deuce's avatar
deuce committed
257
			SAFECOPY(sbbs->terminal,sval);
deuce's avatar
deuce committed
258
			free(sval);
rswindell's avatar
rswindell committed
259
			break;
260 261 262
		case CON_PROP_CTERM_VERSION:
			sbbs->cterm_version = val;
			break;
263 264 265 266 267 268
		case CON_PROP_INACTIV_WARN:
			sbbs->cfg.sec_warn = (uint16_t)val;
			break;
		case CON_PROP_INACTIV_HANGUP:
			sbbs->cfg.sec_hangup = (uint16_t)val;
			break;
269 270 271 272 273 274
		case CON_PROP_TIMEOUT:
			sbbs->timeout=val;
			break;
		case CON_PROP_TIMELEFT_WARN:
			sbbs->timeleft_warn=val;
			break;
275 276 277 278 279 280
		case CON_PROP_ABORTED:
			if(val)
				sbbs->sys_status|=SS_ABORT;
			else
				sbbs->sys_status&=~SS_ABORT;
			break;
rswindell's avatar
rswindell committed
281
		case CON_PROP_ABORTABLE:
282
			sbbs->rio_abortable=val
rswindell's avatar
rswindell committed
283
				? true:false; // This is a dumb bool conversion to make BC++ happy
rswindell's avatar
rswindell committed
284 285 286 287
			break;
		case CON_PROP_TELNET_MODE:
			sbbs->telnet_mode=val;
			break;
288 289 290
		case CON_PROP_GETSTR_OFFSET:
			sbbs->getstr_offset=val;
			break;
291
		case CON_PROP_QUESTION:
deuce's avatar
deuce committed
292
			JSVALUE_TO_MSTRING(cx, *vp, sval, NULL);
deuce's avatar
deuce committed
293
			if(sval==NULL)
294
				break;
deuce's avatar
deuce committed
295
			SAFECOPY(sbbs->question,sval);
deuce's avatar
deuce committed
296
			free(sval);
297
			break;
298
		case CON_PROP_CTRLKEY_PASSTHRU:
299 300 301
			if(JSVAL_IS_STRING(*vp)) {
				char *s;

302 303
				if((str=JS_ValueToString(cx, *vp))==NULL)
					break;
deuce's avatar
deuce committed
304 305 306
				JSSTRING_TO_MSTRING(cx, str, s, NULL);
				if(s==NULL)
					break;
307
				val=str_to_bits(sbbs->cfg.ctrlkey_passthru, s);
deuce's avatar
deuce committed
308
				free(s);
309
			}
310 311
			sbbs->cfg.ctrlkey_passthru=val;
			break;
312

313 314 315 316 317 318 319
		default:
			return(JS_TRUE);
	}

	return(JS_TRUE);
}

rswindell's avatar
rswindell committed
320
#define CON_PROP_FLAGS JSPROP_ENUMERATE
321

322 323 324 325 326
static jsSyncPropertySpec js_console_properties[] = {
/*		 name				,tinyid						,flags			,ver	*/

	{	"status"			,CON_PROP_STATUS			,CON_PROP_FLAGS	,310},
	{	"line_counter"		,CON_PROP_LNCNTR 			,CON_PROP_FLAGS	,310},
327
	{	"current_column"	,CON_PROP_COLUMN			,CON_PROP_FLAGS ,315},
328
	{	"last_line_length"	,CON_PROP_LASTLINELEN		,CON_PROP_FLAGS	,317},
329 330 331 332
	{	"attributes"		,CON_PROP_ATTR				,CON_PROP_FLAGS	,310},
	{	"top_of_screen"		,CON_PROP_TOS				,CON_PROP_FLAGS	,310},
	{	"screen_rows"		,CON_PROP_ROWS				,CON_PROP_FLAGS	,310},
	{	"screen_columns"	,CON_PROP_COLUMNS			,CON_PROP_FLAGS	,311},
rswindell's avatar
rswindell committed
333
	{	"tabstop"			,CON_PROP_TABSTOP			,CON_PROP_FLAGS	,31700},
334 335
	{	"autoterm"			,CON_PROP_AUTOTERM			,CON_PROP_FLAGS	,310},
	{	"terminal"			,CON_PROP_TERMINAL			,CON_PROP_FLAGS ,311},
336
	{	"cterm_version"		,CON_PROP_CTERM_VERSION		,CON_PROP_FLAGS ,317},
337 338
	{	"inactivity_warning",CON_PROP_INACTIV_WARN		,CON_PROP_FLAGS, 31401},
	{	"inactivity_hangup"	,CON_PROP_INACTIV_HANGUP	,CON_PROP_FLAGS, 31401},
339 340 341 342 343 344 345 346 347
	{	"timeout"			,CON_PROP_TIMEOUT			,CON_PROP_FLAGS	,310},
	{	"timeleft_warning"	,CON_PROP_TIMELEFT_WARN		,CON_PROP_FLAGS	,310},
	{	"aborted"			,CON_PROP_ABORTED			,CON_PROP_FLAGS	,310},
	{	"abortable"			,CON_PROP_ABORTABLE			,CON_PROP_FLAGS	,310},
	{	"telnet_mode"		,CON_PROP_TELNET_MODE		,CON_PROP_FLAGS	,310},
	{	"wordwrap"			,CON_PROP_WORDWRAP			,JSPROP_ENUMERATE|JSPROP_READONLY ,310},
	{	"question"			,CON_PROP_QUESTION			,CON_PROP_FLAGS ,310},
	{	"getstr_offset"		,CON_PROP_GETSTR_OFFSET		,CON_PROP_FLAGS ,311},
	{	"ctrlkey_passthru"	,CON_PROP_CTRLKEY_PASSTHRU	,CON_PROP_FLAGS	,310},
348 349 350 351
	{	"input_buffer_level",CON_PROP_INBUF_LEVEL		,JSPROP_ENUMERATE|JSPROP_READONLY, 312},
	{	"input_buffer_space",CON_PROP_INBUF_SPACE		,JSPROP_ENUMERATE|JSPROP_READONLY, 312},
	{	"output_buffer_level",CON_PROP_OUTBUF_LEVEL		,JSPROP_ENUMERATE|JSPROP_READONLY, 312},
	{	"output_buffer_space",CON_PROP_OUTBUF_SPACE		,JSPROP_ENUMERATE|JSPROP_READONLY, 312},
352 353 354
	{0}
};

355
#ifdef BUILD_JSDOCS
356
static const char* con_prop_desc[] = {
357
	 "status bit-field (see <tt>CON_*</tt> in <tt>sbbsdefs.js</tt> for bit definitions)"
358 359
	,"current 0-based line counter (used for automatic screen pause)"
	,"current 0-based column counter (used to auto-increment <i>line_counter</i> when screen wraps)"
360
	,"length of last line sent to terminal (before a carriage-return or line-wrap)"
361
	,"current display attributes (set with number or string value)"
362 363 364
	,"set to <i>true</i> if the terminal cursor is already at the top of the screen"
	,"number of remote terminal screen rows (in lines)"
	,"number of remote terminal screen columns (in character cells)"
rswindell's avatar
rswindell committed
365
	,"current tab stop interval (tab size), in columns"
366
	,"bit-field of automatically detected terminal settings "
367
		"(see <tt>USER_*</tt> in <tt>sbbsdefs.js</tt> for bit definitions)"
rswindell's avatar
rswindell committed
368
	,"terminal type description (e.g. 'ANSI')"
369
	,"detected CTerm (SyncTERM) version as an integer > 1000 where major version is cterm_version / 1000 and minor version is cterm_version % 1000"
370 371 372 373
	,"number of seconds before displaying warning (Are you really there?) due to user/keyboard inactivity"
	,"number of seconds before disconnection due to user/keyboard inactivity"
	,"user/keyboard inactivity timeout reference value (time_t format)"
	,"number of low time-left (5 or fewer minutes remaining) warnings displayed to user"
374
	,"input/output has been aborted"
375
	,"remote output can be asynchronously aborted with Ctrl-C"
376
	,"current Telnet mode bit-field (see <tt>TELNET_MODE_*</tt> in <tt>sbbsdefs.js</tt> for bit definitions)"
377
	,"word-wrap buffer (used by getstr) - <small>READ ONLY</small>"
378
	,"current yes/no question (set by yesno and noyes)"
379
	,"cursor position offset for use with <tt>getstr(K_USEOFFSET)</tt>"
380
	,"control key pass-through bit-mask, set bits represent control key combinations "
381 382
		"<i>not</i> handled by <tt>inkey()</tt> method "
		"This may optionally be specified as a string of characters. "
deuce's avatar
deuce committed
383
		"The format of this string is [+-][@-_]. If neither plus nor minus is "
384
		"the first character, the value will be replaced by one constructed "
deuce's avatar
deuce committed
385
		"from the string. A + indicates that characters following will be "
386
		"added to the set, and a - indicates they should be removed. "
rswindell's avatar
rswindell committed
387
		"ex: <tt>console.ctrlkey_passthru=\"-UP+AB\"</tt> will clear CTRL-U and "
388
		"CTRL-P and set CTRL-A and CTRL-B."
389 390 391 392
	,"number of bytes currently in the input buffer (from the remote client) - <small>READ ONLY</small>"
	,"number of bytes available in the input buffer	- <small>READ ONLY</small>"
	,"number of bytes currently in the output buffer (from the local server) - <small>READ ONLY</small>"
	,"number of bytes available in the output buffer - <small>READ ONLY</small>"
393
	,NULL
394 395 396
};
#endif

397 398 399 400 401
/**************************/
/* Console Object Methods */
/**************************/

static JSBool
402
js_inkey(JSContext *cx, uintN argc, jsval *arglist)
403
{
404
	jsval *argv=JS_ARGV(cx, arglist);
405
	char		key[2];
406 407
	int32		mode=0;
	int32		timeout=0;
408 409
	sbbs_t*		sbbs;
    JSString*	js_str;
deuce's avatar
deuce committed
410
	jsrefcount	rc;
411

412 413
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

414
	if((sbbs=(sbbs_t*)JS_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist)))==NULL)
415 416
		return(JS_FALSE);

417 418 419 420 421 422 423 424
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&mode))
			return JS_FALSE;
	}
	if(argc>1) {
		if(!JS_ValueToInt32(cx,argv[1],&timeout))
			return JS_FALSE;
	}
425
	rc=JS_SUSPENDREQUEST(cx);
426
	key[0]=sbbs->inkey(mode,timeout);
427
	JS_RESUMEREQUEST(cx, rc);
428 429
	key[1]=0;

430 431 432
	if((js_str = JS_NewStringCopyZ(cx, key))==NULL)
		return(JS_FALSE);

433
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
434 435 436 437
    return(JS_TRUE);
}

static JSBool
438
js_getkey(JSContext *cx, uintN argc, jsval *arglist)
439
{
440
	jsval *argv=JS_ARGV(cx, arglist);
441
	char		key[2];
442
	int32		mode=0;
443 444
	sbbs_t*		sbbs;
    JSString*	js_str;
deuce's avatar
deuce committed
445
	jsrefcount	rc;
446

447 448
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

449
	if((sbbs=(sbbs_t*)JS_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist)))==NULL)
450 451
		return(JS_FALSE);

452 453 454 455
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&mode))
			return JS_FALSE;
	}
456
	rc=JS_SUSPENDREQUEST(cx);
457
	key[0]=sbbs->getkey(mode);
458
	JS_RESUMEREQUEST(cx, rc);
459 460
	key[1]=0;

461 462 463
	if((js_str = JS_NewStringCopyZ(cx, key))==NULL)
		return(JS_FALSE);

464
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
465 466 467
    return(JS_TRUE);
}

rswindell's avatar
rswindell committed
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
static JSBool
js_getbyte(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *argv=JS_ARGV(cx, arglist);
	int32		timeout=0;
	sbbs_t*		sbbs;
	jsrefcount	rc;

	JS_SET_RVAL(cx, arglist, JSVAL_NULL);

	if((sbbs=(sbbs_t*)JS_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist)))==NULL)
		return JS_FALSE;

	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0], &timeout))
			return JS_FALSE;
	}
	rc=JS_SUSPENDREQUEST(cx);
	int32 byte = sbbs->incom(timeout);
	JS_RESUMEREQUEST(cx, rc);

	if(byte != NOINP)
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(byte));
    return JS_TRUE;
}

static JSBool
js_putbyte(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval *argv=JS_ARGV(cx, arglist);
	int32		byte=0;
	sbbs_t*		sbbs;
	jsrefcount	rc;

	if((sbbs=(sbbs_t*)JS_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist)))==NULL)
		return JS_FALSE;

	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0], &byte))
			return JS_FALSE;
	}
	rc=JS_SUSPENDREQUEST(cx);
	int result = sbbs->outcom(byte);
	JS_RESUMEREQUEST(cx, rc);

	JS_SET_RVAL(cx, arglist, result == 0 ? JSVAL_TRUE : JSVAL_FALSE);
    return JS_TRUE ;
}


rswindell's avatar
rswindell committed
518
static JSBool
519
js_handle_ctrlkey(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
520
{
521
	jsval *argv=JS_ARGV(cx, arglist);
522
	char		key;
rswindell's avatar
rswindell committed
523
	int32		mode=0;
rswindell's avatar
rswindell committed
524
	sbbs_t*		sbbs;
deuce's avatar
deuce committed
525
	jsrefcount	rc;
deuce's avatar
deuce committed
526
	char		*keystr;
rswindell's avatar
rswindell committed
527

528
	JS_SET_RVAL(cx, arglist, JSVAL_FALSE);
rswindell's avatar
rswindell committed
529

530
	if((sbbs=(sbbs_t*)JS_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist)))==NULL)
rswindell's avatar
rswindell committed
531 532
		return(JS_FALSE);

533 534 535
	if(JSVAL_IS_INT(argv[0]))
		key=(char)JSVAL_TO_INT(argv[0]);
	else {
deuce's avatar
deuce committed
536
		JSVALUE_TO_ASTRING(cx, argv[0], keystr, 2, NULL);
deuce's avatar
deuce committed
537
		if(keystr==NULL)
538
			return(JS_FALSE);
deuce's avatar
deuce committed
539
		key=keystr[0];
540
	}
rswindell's avatar
rswindell committed
541

542 543 544 545
	if(argc>1) {
		if(!JS_ValueToInt32(cx, argv[1], &mode))
			return JS_FALSE;
	}
rswindell's avatar
rswindell committed
546

547
	rc=JS_SUSPENDREQUEST(cx);
548
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->handle_ctrlkey(key,mode)==0));
549
	JS_RESUMEREQUEST(cx, rc);
rswindell's avatar
rswindell committed
550 551 552
    return(JS_TRUE);
}

553
static JSBool
554
js_getstr(JSContext *cx, uintN argc, jsval *arglist)
555
{
556
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
557
	char		*p,*p2;
558
	int32		mode=0;
559
	uintN		i;
560
	int32		maxlen=0;
561 562
	sbbs_t*		sbbs;
    JSString*	js_str=NULL;
deuce's avatar
deuce committed
563
	jsrefcount	rc;
564
	str_list_t	history = NULL;
565

566 567
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

568
	if((sbbs=(sbbs_t*)JS_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist)))==NULL)
569 570 571
		return(JS_FALSE);

	for(i=0;i<argc;i++) {
572
		if(JSVAL_IS_NUMBER(argv[i])) {
573
			if(!maxlen) {
574
				if(!JS_ValueToInt32(cx,argv[i],&maxlen))
575 576 577
					return JS_FALSE;
			}
			else {
578
				if(!JS_ValueToInt32(cx,argv[i],&mode))
579 580
					return JS_FALSE;
			}
581
		}
582
		else if(JSVAL_IS_STRING(argv[i])) {
583 584 585 586
			js_str = JS_ValueToString(cx, argv[i]);
			if (!js_str)
			    return(JS_FALSE);
		}
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
		else if(JSVAL_IS_OBJECT(argv[i])) {
			JSObject* array = JSVAL_TO_OBJECT(argv[i]);
			jsuint len=0;
			if(!JS_GetArrayLength(cx, array, &len))
				return JS_FALSE;
			history = (str_list_t)alloca(sizeof(char*) * (len + 1));
			memset(history, 0, sizeof(char*) * (len + 1));
			for(jsuint j=0; j < len; j++) {
				jsval		val;
				if(!JS_GetElement(cx, array, j, &val))
					break;
				JSString* hist = JS_ValueToString(cx, val);
				if (hist == NULL)
					return JS_FALSE;
				char* cstr = NULL;
				JSSTRING_TO_ASTRING(cx, hist, cstr, (uint)(maxlen ? maxlen : 80), NULL);
				if(cstr == NULL)
					return JS_FALSE;
				history[j] = cstr;
			}
		}
608 609 610 611 612 613 614
	}

	if(!maxlen) maxlen=128;

	if((p=(char *)calloc(1,maxlen+1))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
615
	if(js_str!=NULL) {
deuce's avatar
deuce committed
616
		JSSTRING_TO_MSTRING(cx, js_str, p2, NULL);
617 618
		if(p2==NULL) {
			free(p);
deuce's avatar
deuce committed
619
			return JS_FALSE;
620
		}
deuce's avatar
deuce committed
621
		sprintf(p,"%.*s",(int)maxlen,p2);
deuce's avatar
deuce committed
622
		free(p2);
deuce's avatar
deuce committed
623
	}
624

625
	rc=JS_SUSPENDREQUEST(cx);
626
	sbbs->getstr(p, maxlen, mode, history);
627
	JS_RESUMEREQUEST(cx, rc);
628 629 630 631 632

	js_str = JS_NewStringCopyZ(cx, p);

	free(p);

633 634 635
	if(js_str==NULL)
		return(JS_FALSE);

636
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
637 638 639 640
    return(JS_TRUE);
}

static JSBool
641
js_getnum(JSContext *cx, uintN argc, jsval *arglist)
642
{
643
	jsval *argv=JS_ARGV(cx, arglist);
644
	uint32_t	maxnum=~0;
645
	int32_t		dflt=0;
646
	sbbs_t*		sbbs;
deuce's avatar
deuce committed
647
	jsrefcount	rc;
648

649 650
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

651
	if((sbbs=(sbbs_t*)JS_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist)))==NULL)
652 653
		return(JS_FALSE);

654 655 656 657 658
	if(argc && JSVAL_IS_NUMBER(argv[0])) {
		if(!JS_ValueToInt32(cx,argv[0],(int32*)&maxnum))
			return JS_FALSE;
	}
	if(argc>1 && JSVAL_IS_NUMBER(argv[1])) {
659
		if(!JS_ValueToInt32(cx,argv[1],&dflt))
660 661
			return JS_FALSE;
	}
662

663
	rc=JS_SUSPENDREQUEST(cx);
664
	JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(sbbs->getnum(maxnum,dflt)));
665
	JS_RESUMEREQUEST(cx, rc);
666 667 668 669
    return(JS_TRUE);
}

static JSBool
670
js_getkeys(JSContext *cx, uintN argc, jsval *arglist)
671
{
672
	jsval *argv=JS_ARGV(cx, arglist);
673 674
	char		key[2];
	uintN		i;
675 676
	int32		val;
	uint32		maxnum=~0;
677 678
	sbbs_t*		sbbs;
    JSString*	js_str=NULL;
deuce's avatar
deuce committed
679 680
	char*		cstr;
	jsrefcount	rc;
681

682 683
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

684
	if((sbbs=(sbbs_t*)JS_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist)))==NULL)
685 686 687
		return(JS_FALSE);

	for(i=0;i<argc;i++) {
688
		if(JSVAL_IS_NUMBER(argv[i])) {
689 690
			if(!JS_ValueToInt32(cx,argv[i],(int32*)&maxnum))
				return JS_FALSE;
691 692 693 694 695 696
			continue;
		}
		if(JSVAL_IS_STRING(argv[i])) {
			js_str = JS_ValueToString(cx, argv[i]);
		}
	}
697
	if(js_str==NULL)
698 699
		return(JS_FALSE);

deuce's avatar
deuce committed
700 701 702
	JSSTRING_TO_MSTRING(cx, js_str, cstr, NULL);
	if(cstr==NULL)
		return JS_FALSE;
703
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
704
	val=sbbs->getkeys(cstr,maxnum);
deuce's avatar
deuce committed
705
	free(cstr);
706
	JS_RESUMEREQUEST(cx, rc);
707 708

	if(val==-1) {			// abort
709
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(0));
710 711
	} else if(val<0) {		// number
		val&=~0x80000000;
712
		JS_SET_RVAL(cx, arglist, INT_TO_JSVAL(val));
713 714 715
	} else {				// key
		key[0]=(uchar)val;
		key[1]=0;
716 717
		if((js_str = JS_NewStringCopyZ(cx, key))==NULL)
			return(JS_FALSE);
718
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
719 720 721 722 723 724
	}

    return(JS_TRUE);
}

static JSBool
725
js_gettemplate(JSContext *cx, uintN argc, jsval *arglist)
726
{
727
	jsval *argv=JS_ARGV(cx, arglist);
728
	char		str[128];
729
	int32		mode=0;
730 731 732 733
	uintN		i;
	sbbs_t*		sbbs;
    JSString*	js_str=NULL;
    JSString*	js_fmt=NULL;
deuce's avatar
deuce committed
734 735
	jsrefcount	rc;
	char*		cstr;
736

737 738
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

739
	if((sbbs=(sbbs_t*)JS_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist)))==NULL)
740 741 742 743 744 745 746 747
		return(JS_FALSE);

	for(i=0;i<argc;i++) {
		if(JSVAL_IS_STRING(argv[i])) {
			if(js_fmt==NULL)
				js_fmt = JS_ValueToString(cx, argv[i]);
			else
				js_str = JS_ValueToString(cx, argv[i]);
748 749 750 751
		} else if(JSVAL_IS_NUMBER(argv[i])) {
			if(!JS_ValueToInt32(cx,argv[i],(int32*)&mode))
				return JS_FALSE;
		}
752 753 754 755 756 757 758
	}

	if(js_fmt==NULL)
		return(JS_FALSE);

	if(js_str==NULL)
		str[0]=0;
deuce's avatar
deuce committed
759
	else {
deuce's avatar
deuce committed
760
		JSSTRING_TO_STRBUF(cx, js_str, str, sizeof(str), NULL);
deuce's avatar
deuce committed
761
	}
762

deuce's avatar
deuce committed
763 764 765
	JSSTRING_TO_MSTRING(cx, js_fmt, cstr, NULL);
	if(cstr==NULL)
		return JS_FALSE;
766
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
767
	sbbs->gettmplt(str,cstr,mode);
deuce's avatar
deuce committed
768
	free(cstr);
769
	JS_RESUMEREQUEST(cx, rc);
770 771 772 773

	if((js_str=JS_NewStringCopyZ(cx, str))==NULL)
		return(JS_FALSE);

774
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(js_str));
775 776 777 778
    return(JS_TRUE);
}

static JSBool
779
js_ungetstr(JSContext *cx, uintN argc, jsval *arglist)
780
{
781
	jsval *argv=JS_ARGV(cx, arglist);
782
	char*		p;
deuce's avatar
deuce committed
783
	char*		op;
784 785
	sbbs_t*		sbbs;
    JSString*	js_str;
deuce's avatar
deuce committed
786
	jsrefcount	rc;
787

788 789
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

790
	if((sbbs=(sbbs_t*)JS_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist)))==NULL)
791
		return(JS_FALSE);
792

793 794 795
	if((js_str=JS_ValueToString(cx, argv[0]))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
796 797 798
	JSSTRING_TO_MSTRING(cx, js_str, op, NULL);
	if(op==NULL)
		return JS_FALSE;
799

deuce's avatar
deuce committed
800
	p=op;
801
	rc=JS_SUSPENDREQUEST(cx);
802 803
	while(p && *p)
		sbbs->ungetkey(*(p++));
deuce's avatar
deuce committed
804
	free(op);
805
	JS_RESUMEREQUEST(cx, rc);
806 807 808 809
    return(JS_TRUE);
}

static JSBool
810
js_yesno(JSContext *cx, uintN argc, jsval *arglist)
811
{
812
	jsval *argv=JS_ARGV(cx, arglist);
813 814
	sbbs_t*		sbbs;
    JSString*	js_str;
deuce's avatar
deuce committed
815 816
	char*		cstr;
	jsrefcount	rc;
817

818 819
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

820
	if((sbbs=(sbbs_t*)JS_GetPrivate(cx, JS_THIS_OBJECT(cx, arglist)))==NULL)
821
		return(JS_FALSE);
822

823 824 825
	if((js_str=JS_ValueToString(cx, argv[0]))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
826 827 828
	JSSTRING_TO_MSTRING(cx, js_str, cstr, NULL);
	if(cstr==NULL)
		return JS_FALSE;
829
	rc=JS_SUSPENDREQUEST(cx);
830
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->yesno(cstr)));
deuce's avatar
deuce committed
831
	free(cstr);
832
	JS_RESUMEREQUEST(cx, rc);
833 834 835 836
    return(JS_TRUE);
}

static JSBool
837
js_noyes(JSContext *cx, uintN argc, jsval *arglist)
838
{
839
	jsval *argv=JS_ARGV(cx, arglist);
840 841
	sbbs_t*		sbbs;
    JSString*	js_str;
deuce's avatar
deuce committed
842 843
	char*		cstr;
	jsrefcount	rc;
844