main.cpp 160 KB
Newer Older
1
/* Synchronet terminal server thread and related functions */
2
3

/* $Id$ */
4
// vi: tabstop=4
5
6
7
8
9

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11
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
39
 *																			*
 * 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"
#include "ident.h"
#include "telnet.h" 
40
#include "netwrap.h"
41
#include "js_rtpool.h"
42
#include "js_request.h"
43
#include "ssl.h"
deuce's avatar
deuce committed
44
45
#include <multisock.h>
#include <limits.h>		// HOST_NAME_MAX
46
47
48
49
50

#ifdef __unix__
	#include <sys/un.h>
#endif

51
//#define SBBS_TELNET_ENVIRON_SUPPORT 1
52
53
//---------------------------------------------------------------------------

54
#define TELNET_SERVER "Synchronet Terminal Server"
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#define STATUS_WFC	"Listening"

#define TIMEOUT_THREAD_WAIT		60			// Seconds (was 15)
#define IO_THREAD_BUF_SIZE	   	20000		// Bytes

// Globals
#ifdef _WIN32
	HANDLE		exec_mutex=NULL;
	HINSTANCE	hK32=NULL;

	#if defined(_DEBUG) && defined(_MSC_VER)
			HANDLE	debug_log=INVALID_HANDLE_VALUE;
		   _CrtMemState mem_chkpoint;
	#endif // _DEBUG && _MSC_VER

#endif // _WIN32

72
#ifdef USE_CRYPTLIB
73
	#define SSH_END()	if(ssh) { pthread_mutex_lock(&sbbs->ssh_mutex); cryptDestroySession(sbbs->ssh_session); pthread_mutex_unlock(&sbbs->ssh_mutex); }
74
75
76
77
#else
	#define	SSH_END()
#endif

78
79
volatile time_t	uptime=0;
volatile ulong	served=0;
80

81
static	protected_uint32_t node_threads_running;
82
83
84
85
86
87
88
89
		
char 	lastuseron[LEN_ALIAS+1];  /* Name of user last online */
RingBuf* node_inbuf[MAX_NODES];
SOCKET	spy_socket[MAX_NODES];
#ifdef __unix__
SOCKET	uspy_socket[MAX_NODES];	  /* UNIX domain spy sockets */
#endif
SOCKET	node_socket[MAX_NODES];
deuce's avatar
deuce committed
90
struct xpms_set				*ts_set;
91
92
93
94
95
static	sbbs_t*	sbbs=NULL;
static	scfg_t	scfg;
static	char *	text[TOTAL_TEXT];
static	WORD	first_node;
static	WORD	last_node;
96
static	bool	terminate_server=false;
97
98
static	str_list_t recycle_semfiles;
static	str_list_t shutdown_semfiles;
99
100
101
#ifdef _THREAD_SUID_BROKEN
int	thread_suid_broken=TRUE;			/* NPTL is no longer broken */
#endif
102
103
104
105
106

extern "C" {

static bbs_startup_t* startup=NULL;

rswindell's avatar
rswindell committed
107
static const char* status(const char* str)
108
109
{
	if(startup!=NULL && startup->status!=NULL)
110
	    startup->status(startup->cbdata,str);
rswindell's avatar
rswindell committed
111
	return str;
112
113
114
115
116
}

static void update_clients()
{
	if(startup!=NULL && startup->clients!=NULL)
117
		startup->clients(startup->cbdata,protected_uint32_value(node_threads_running));
118
119
120
121
122
}

void client_on(SOCKET sock, client_t* client, BOOL update)
{
	if(startup!=NULL && startup->client_on!=NULL)
123
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
124
125
126
127
128
}

static void client_off(SOCKET sock)
{
	if(startup!=NULL && startup->client_on!=NULL)
129
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
130
131
132
133
134
}

static void thread_up(BOOL setuid)
{
	if(startup!=NULL && startup->thread_up!=NULL)
135
		startup->thread_up(startup->cbdata,TRUE,setuid);
136
137
138
139
140
}

static void thread_down()
{
	if(startup!=NULL && startup->thread_up!=NULL)
141
		startup->thread_up(startup->cbdata,FALSE,FALSE);
142
143
}

144
int lputs(int level, const char* str)
145
{
146
	if(level <= LOG_ERR) {
147
		errorlog(&scfg,startup==NULL ? NULL:startup->host_name, str);
148
149
150
		if(startup!=NULL && startup->errormsg!=NULL)
			startup->errormsg(startup->cbdata,level,str);
	}
151

152
	if(startup==NULL || startup->lputs==NULL || str==NULL || level > startup->log_level)
153
154
    	return(0);

155
156
157
158
159
#if defined(_WIN32)
	if(IsBadCodePtr((FARPROC)startup->lputs))
		return(0);
#endif

160
    return(startup->lputs(startup->cbdata,level,str));
161
162
}

163
int lprintf(int level, const char *fmt, ...)
164
165
166
167
168
169
170
171
{
	va_list argptr;
	char sbuf[1024];

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
172
    return(lputs(level,sbuf));
173
174
}

175
int eprintf(int level, const char *fmt, ...)
176
177
178
179
180
181
182
183
{
	va_list argptr;
	char sbuf[1024];

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
184
185
186
187
188
189
190
191
192
193

	if(level <= LOG_ERR) {
		errorlog(&scfg,startup==NULL ? NULL:startup->host_name, sbuf);
		if(startup!=NULL && startup->errormsg!=NULL)
			startup->errormsg(startup->cbdata,level,sbuf);
	}

    if(startup==NULL || startup->event_lputs==NULL || level > startup->log_level)
        return(0);

194
	strip_ctrl(sbuf, sbuf);
195
    return(startup->event_lputs(startup->event_cbdata,level,sbuf));
196
197
}

deuce's avatar
deuce committed
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
struct main_sock_cb_data {
	bbs_startup_t	*startup;
	const char		*protocol;
};

void sock_cb(SOCKET sock, void *cb_data)
{
	char	error_str[256];
	struct main_sock_cb_data *cb=(struct main_sock_cb_data *)cb_data;

	if(cb->startup && cb->startup->socket_open)
		cb->startup->socket_open(cb->startup->cbdata, TRUE);
	if(set_socket_options(&scfg, sock, cb->protocol, error_str, sizeof(error_str)))
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error_str);
}

void sock_close_cb(SOCKET sock, void *cb_data)
{
	bbs_startup_t	*su=(bbs_startup_t *)cb_data;

	if(su && su->socket_open)
		su->socket_open(su->cbdata, FALSE);
}

222
SOCKET open_socket(int type, const char* protocol)
223
224
225
226
227
228
{
	SOCKET	sock;
	char	error[256];

	sock=socket(AF_INET, type, IPPROTO_IP);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
229
		startup->socket_open(startup->cbdata,TRUE);
230
	if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
231
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
232
233
234
235

	return(sock);
}

deuce's avatar
deuce committed
236
SOCKET accept_socket(SOCKET s, union xp_sockaddr* addr, socklen_t* addrlen)
237
238
239
{
	SOCKET	sock;

deuce's avatar
deuce committed
240
241
	sock=accept(s,&addr->addr,addrlen);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
242
		startup->socket_open(startup->cbdata,TRUE);
243
244
245
246
247
248
249
250
251
252
253
254
255

	return(sock);
}

int close_socket(SOCKET sock)
{
	int		result;

	if(sock==INVALID_SOCKET || sock==0)
		return(0);

	shutdown(sock,SHUT_RDWR);	/* required on Unix */
	result=closesocket(sock);
256
	if(startup!=NULL && startup->socket_open!=NULL)
257
		startup->socket_open(startup->cbdata,FALSE);
258
	if(result!=0 && ERROR_VALUE!=ENOTSOCK)
259
		lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
260
261
262
	return(result);
}

deuce's avatar
deuce committed
263
/* TODO: IPv6 */
264
265
266
267
268
269
u_long resolve_ip(char *addr)
{
	HOSTENT*	host;
	char*		p;

	if(*addr==0)
270
		return((u_long)INADDR_NONE);
271
272

	for(p=addr;*p;p++)
273
		if(*p!='.' && !isdigit((uchar)*p))
274
275
276
277
			break;
	if(!(*p))
		return(inet_addr(addr));
	if((host=gethostbyname(addr))==NULL) 
278
		return((u_long)INADDR_NONE);
279
280
281
282
283
	return(*((ulong*)host->h_addr_list[0]));
}

} /* extern "C" */

284
285
286
287
288
289
290
291
292
293
294
#ifdef _WINSOCKAPI_

WSADATA WSAData;
#define SOCKLIB_DESC WSAData.szDescription
static BOOL WSAInitialized=FALSE;

static BOOL winsock_startup(void)
{
	int		status;             /* Status Code */

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
295
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
296
297
298
299
		WSAInitialized=TRUE;
		return(TRUE);
	}

300
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
301
302
303
304
305
306
307
308
309
310
	return(FALSE);
}

#else /* No WINSOCK */

#define winsock_startup()	(TRUE)
#define SOCKLIB_DESC NULL

#endif

311
312
DLLEXPORT void DLLCALL sbbs_srand()
{
313
	DWORD seed;
314

315
	xp_randomize();
316
#if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
317
	int     rf,rd=0;
318

319
320
	if((rf=open(RANDOM_DEV, O_RDONLY|O_NONBLOCK))!=-1) {
		rd=read(rf, &seed, sizeof(seed));
321
322
		close(rf);
	}
deuce's avatar
deuce committed
323
	if (rd != sizeof(seed))
324
#endif
325
		seed = time32(NULL) ^ (uintmax_t)GetCurrentThreadId();
326
327

 	srand(seed);
328
329
330
	sbbs_random(10);	/* Throw away first number */
}

331
int DLLCALL sbbs_random(int n)
332
333
334
335
{
	return(xp_random(n));
}

336
337
#ifdef JAVASCRIPT

338
339
static js_server_props_t js_server_props;

340
341
342
343
344
345
346
347
348
349
350
351
JSBool	
DLLCALL js_CreateArrayOfStrings(JSContext* cx, JSObject* parent, const char* name, char* str[],uintN flags)
{
	JSObject*	array;
	JSString*	js_str;
	jsval		val;
	size_t		i;
	jsuint		len=0;
		
	if(JS_GetProperty(cx,parent,name,&val) && val!=JSVAL_VOID)
		array=JSVAL_TO_OBJECT(val);
	else
352
353
		if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)	/* Assertion here, in _heap_alloc_dbg, June-21-2004 */
			return(JS_FALSE);								/* Caused by nntpservice.js? */
354

355
356
357
358
	if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
		,NULL,NULL,flags))
		return(JS_FALSE);

359
360
361
362
363
364
365
366
367
368
369
	if(!JS_GetArrayLength(cx, array, &len))
		return(JS_FALSE);

	for(i=0;str[i]!=NULL;i++) {
		if((js_str = JS_NewStringCopyZ(cx, str[i]))==NULL)
			break;
		val = STRING_TO_JSVAL(js_str);
		if(!JS_SetElement(cx, array, len+i, &val))
			break;
	}

370
	return(JS_TRUE);
371
372
}

373
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
374
375

JSBool
376
DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
377
378
379
380
381
382
{
	JSString* js_str = JS_NewStringCopyZ(cx, str);

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

rswindell's avatar
rswindell committed
383
384
385
	if(ver < 10000)		/* auto convert 313 to 31300 */
		ver*=100;

386
	return(JS_DefineProperty(cx,obj,"_description"
387
388
389
			,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
		&& JS_DefineProperty(cx,obj,"_ver"
			,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
390
391
392
}

JSBool
393
DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
394
395
396
397
398
399
400
401
402
403
{
	JSString* js_str = JS_NewStringCopyZ(cx, str);

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

	return(JS_DefineProperty(cx,obj,"_constructor"
		,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY));
}

404
#ifdef BUILD_JSDOCS
405
406

static const char* method_array_name = "_method_list";
407
static const char* propver_array_name = "_property_ver_list";
408
409
410
411
412
413
414
415
416
417
418
419
420

/*
 * from jsatom.c:
 * Keep this in sync with jspubtd.h -- an assertion below will insist that
 * its length match the JSType enum's JSTYPE_LIMIT limit value.
 */
static const char *js_type_str[] = {
    "void",			// changed from "undefined"
    "object",
    "function",
    "string",
    "number",
    "boolean",
deuce's avatar
deuce committed
421
422
	"null",
	"xml",
423
424
	"array",
	"alias",
deuce's avatar
deuce committed
425
	"undefined"
426
427
};

428
429
430
431
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint		i;
432
	long		ver;
433
434
435
436
437
438
439
440
441
442
443
444
	jsval		val;
	jsuint		len=0;
	JSObject*	array;

	if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)
		return(JS_FALSE);

	if(!JS_DefineProperty(cx, obj, propver_array_name, OBJECT_TO_JSVAL(array)
		,NULL,NULL,JSPROP_READONLY))
		return(JS_FALSE);

	for(i=0;props[i].name;i++) {
445
446
		if(!JS_DefinePropertyWithTinyId(cx, obj, /* Never reserve any "slots" for properties */
			props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
447
448
			return(JS_FALSE);
		if(props[i].flags&JSPROP_ENUMERATE) {	/* No need to version invisible props */
449
450
451
			if((ver=props[i].ver) < 10000)		/* auto convert 313 to 31300 */
				ver*=100;
			val = INT_TO_JSVAL(ver);
452
453
454
455
456
457
458
459
			if(!JS_SetElement(cx, array, len++, &val))
				return(JS_FALSE);
		}
	}

	return(JS_TRUE);
}

460
JSBool 
deuce's avatar
deuce committed
461
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
462
463
464
{
	int			i;
	jsuint		len=0;
465
	long		ver;
466
467
468
469
	jsval		val;
	JSObject*	method;
	JSObject*	method_array;
	JSString*	js_str;
deuce's avatar
deuce committed
470
471
	size_t		str_len=0;
	char		*str=NULL;
472
473

	/* Return existing method_list array if it's already been created */
deuce's avatar
deuce committed
474
	if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID) {
475
		method_array=JSVAL_TO_OBJECT(val);
deuce's avatar
deuce committed
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
		// If the first item is already in the list, don't do anything.
		if(!JS_GetArrayLength(cx, method_array, &len))
			return(JS_FALSE);
		for(i=0; i<len; i++) {
			if(JS_GetElement(cx, method_array, i, &val)!=JS_TRUE || val == JSVAL_VOID)
				continue;
			JS_GetProperty(cx, JSVAL_TO_OBJECT(val), "name", &val);
			JSVALUE_TO_RASTRING(cx, val, str, &str_len, NULL);
			if(str==NULL)
				continue;
			if(strcmp(str, funcs[0].name)==0)
				return(JS_TRUE);
		}
	}
	else {
491
492
		if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL) 
			return(JS_FALSE);
deuce's avatar
deuce committed
493
494
		if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
				, NULL, NULL, 0))
495
			return(JS_FALSE);
deuce's avatar
deuce committed
496
	}
497
498
499

	for(i=0;funcs[i].name;i++) {

500
501
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
502
503
504
505

		if(funcs[i].type==JSTYPE_ALIAS)
			continue;

506
		method = JS_NewObject(cx, NULL, NULL, method_array);	/* exception here June-7-2003 */
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540

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

		if(funcs[i].name!=NULL) {
			if((js_str=JS_NewStringCopyZ(cx,funcs[i].name))==NULL)
				return(JS_FALSE);
			val = STRING_TO_JSVAL(js_str);
			JS_SetProperty(cx, method, "name", &val);
		}

		val = INT_TO_JSVAL(funcs[i].nargs);
		if(!JS_SetProperty(cx, method, "nargs", &val))
			return(JS_FALSE);

		if((js_str=JS_NewStringCopyZ(cx,js_type_str[funcs[i].type]))==NULL)
			return(JS_FALSE);
		val = STRING_TO_JSVAL(js_str);
		JS_SetProperty(cx, method, "type", &val);

		if(funcs[i].args!=NULL) {
			if((js_str=JS_NewStringCopyZ(cx,funcs[i].args))==NULL)
				return(JS_FALSE);
			val = STRING_TO_JSVAL(js_str);
			JS_SetProperty(cx, method, "args", &val);
		}

		if(funcs[i].desc!=NULL) { 
			if((js_str=JS_NewStringCopyZ(cx,funcs[i].desc))==NULL)
				return(JS_FALSE);
			val = STRING_TO_JSVAL(js_str);
			JS_SetProperty(cx, method, "desc", &val);
		}

541
		if(funcs[i].ver) {
542
			if((ver=funcs[i].ver) < 10000)		/* auto convert 313 to 31300 */
543
544
				ver*=100;
			val = INT_TO_JSVAL(ver);
545
546
547
			JS_SetProperty(cx,method, "ver", &val);
		}

548
549
550
551
552
553
554
555
		val=OBJECT_TO_JSVAL(method);
		if(!JS_SetElement(cx, method_array, len+i, &val))
			return(JS_FALSE);
	}

	return(JS_TRUE);
}

556
557
558
559
560
/*
 * Always resolve all here since
 * 1) We'll always be enumerating anyways
 * 2) The speed penalty won't be seen in production code anyways
 */
561
562
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
563
564
565
{
	JSBool	ret=JS_TRUE;

deuce's avatar
deuce committed
566
	if(props) {
567
568
		if(!js_DefineSyncProperties(cx, obj, props))
			ret=JS_FALSE;
deuce's avatar
deuce committed
569
	}
570
		
deuce's avatar
deuce committed
571
	if(funcs) {
deuce's avatar
deuce committed
572
		if(!js_DefineSyncMethods(cx, obj, funcs))
573
			ret=JS_FALSE;
deuce's avatar
deuce committed
574
	}
575

deuce's avatar
deuce committed
576
577
578
579
	if(consts) {
		if(!js_DefineConstIntegers(cx, obj, consts, flags))
			ret=JS_FALSE;
	}
580

581
582
583
	return(ret);
}

584
#else // NON-JSDOCS
585

586
587
588
589
590
591
592
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint i;

	for(i=0;props[i].name;i++) 
		if(!JS_DefinePropertyWithTinyId(cx, obj, 
593
			props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
594
595
596
597
598
599
			return(JS_FALSE);

	return(JS_TRUE);
}


600
JSBool 
deuce's avatar
deuce committed
601
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
602
{
603
	uint i;
604
605

	for(i=0;funcs[i].name;i++)
606
607
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
608
609
610
	return(JS_TRUE);
}

611
612
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
613
614
{
	uint i;
615
	jsval	val;
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637

	if(props) {
		for(i=0;props[i].name;i++) {
			if(name==NULL || strcmp(name, props[i].name)==0) {
				if(!JS_DefinePropertyWithTinyId(cx, obj, 
						props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
					return(JS_FALSE);
				if(name)
					return(JS_TRUE);
			}
		}
	}
	if(funcs) {
		for(i=0;funcs[i].name;i++) {
			if(name==NULL || strcmp(name, funcs[i].name)==0) {
				if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
					return(JS_FALSE);
				if(name)
					return(JS_TRUE);
			}
		}
	}
638
	if(consts) {
deuce's avatar
deuce committed
639
		for(i=0;consts[i].name;i++) {
640
			if(name==NULL || strcmp(name, consts[i].name)==0) {
641
				val=INT_TO_JSVAL(consts[i].val);
642

deuce's avatar
deuce committed
643
				if(!JS_DefineProperty(cx, obj, consts[i].name, val ,NULL, NULL, flags))
644
645
646
647
648
649
650
					return(JS_FALSE);

				if(name)
					return(JS_TRUE);
			}
		}
	}
651
652
653
654

	return(JS_TRUE);
}

655
656
#endif

657
658
659
660
661
662
663
664
/* This is a stream-lined version of JS_DefineConstDoubles */
JSBool
DLLCALL js_DefineConstIntegers(JSContext* cx, JSObject* obj, jsConstIntSpec* ints, int flags)
{
	uint	i;
	jsval	val;

	for(i=0;ints[i].name;i++) {
665
		val=INT_TO_JSVAL(ints[i].val);
666
667
668
669

		if(!JS_DefineProperty(cx, obj, ints[i].name, val ,NULL, NULL, flags))
			return(JS_FALSE);
	}
670

671
672
673
	return(JS_TRUE);
}

674
static JSBool
675
js_log(JSContext *cx, uintN argc, jsval *arglist)
676
{
677
	jsval *argv=JS_ARGV(cx, arglist);
678
679
    uintN		i=0;
	int32		level=LOG_INFO;
680
    JSString*	str=NULL;
681
	sbbs_t*		sbbs;
682
	jsrefcount	rc;
deuce's avatar
deuce committed
683
684
	char		*line=NULL;
	size_t		line_sz=0;
685

686
687
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

688
689
690
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

691
692
693
694
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
695
696

    for(; i<argc; i++) {
deuce's avatar
deuce committed
697
698
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			return(JS_FALSE);
deuce's avatar
deuce committed
699
		JSSTRING_TO_RASTRING(cx, str, line, &line_sz, NULL);
deuce's avatar
deuce committed
700
		if(line==NULL)
701
		    return(JS_FALSE);
702
		rc=JS_SUSPENDREQUEST(cx);
703
		if(sbbs->online==ON_LOCAL) {
deuce's avatar
deuce committed
704
705
706
			if(startup!=NULL && startup->event_lputs!=NULL && level <= startup->log_level) {
				startup->event_lputs(startup->event_cbdata,level,line);
			}
707
		} else
deuce's avatar
deuce committed
708
			lprintf(level,"Node %d %s", sbbs->cfg.node_num, line);
709
		JS_RESUMEREQUEST(cx, rc);
710
	}
deuce's avatar
deuce committed
711
	free(line);
712

713
	if(str==NULL)
714
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
715
	else
716
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
717
718
719
    return(JS_TRUE);
}

720
static JSBool
721
js_read(JSContext *cx, uintN argc, jsval *arglist)
722
{
723
	jsval *argv=JS_ARGV(cx, arglist);
724
725
726
	uchar*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
727
	jsrefcount	rc;
728

729
730
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

731
732
733
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

734
735
736
737
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
738
739
740
741

	if((buf=(uchar*)malloc(len))==NULL)
		return(JS_TRUE);

742
	rc=JS_SUSPENDREQUEST(cx);
743
	len=RingBufRead(&sbbs->inbuf,buf,len);
744
	JS_RESUMEREQUEST(cx, rc);
745
746

	if(len>0)
747
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len)));
748
749
750
751
752
753

	free(buf);
	return(JS_TRUE);
}

static JSBool
754
js_readln(JSContext *cx, uintN argc, jsval *arglist)
755
{
756
	jsval *argv=JS_ARGV(cx, arglist);
757
758
759
	char*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
760
	jsrefcount	rc;
761

762
763
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

764
765
766
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

767
768
769
770
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
771
772
773
774

	if((buf=(char*)malloc(len))==NULL)
		return(JS_TRUE);

775
	rc=JS_SUSPENDREQUEST(cx);
776
	len=sbbs->getstr(buf,len,K_NONE);
777
	JS_RESUMEREQUEST(cx, rc);
778
779

	if(len>0)
780
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf)));
781
782
783
784
785

	free(buf);
	return(JS_TRUE);
}

786
static JSBool
787
js_write(JSContext *cx, uintN argc, jsval *arglist)
788
{
789
	jsval *argv=JS_ARGV(cx, arglist);
790
    uintN		i;
791
    JSString*	str=NULL;
792
	sbbs_t*		sbbs;
793
	jsrefcount	rc;
794
795
	char		*cstr=NULL;
	size_t		cstr_sz=0;
796

797
798
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

799
800
801
802
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
803
804
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			return(JS_FALSE);
deuce's avatar
deuce committed
805
		JSSTRING_TO_RASTRING(cx, str, cstr, &cstr_sz, NULL);
deuce's avatar
deuce committed
806
		if(cstr==NULL)
807
		    return(JS_FALSE);
808
		rc=JS_SUSPENDREQUEST(cx);
809
		if(sbbs->online==ON_LOCAL)
deuce's avatar
deuce committed
810
			eprintf(LOG_INFO,"%s",cstr);
811
		else
deuce's avatar
deuce committed
812
			sbbs->bputs(cstr);
813
		JS_RESUMEREQUEST(cx, rc);
814
815
816
	}

	if(str==NULL)
817
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
818
	else
819
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
820
821
822
    return(JS_TRUE);
}

823
static JSBool
824
js_write_raw(JSContext *cx, uintN argc, jsval *arglist)
825
{
826
	jsval *argv=JS_ARGV(cx, arglist);
827
    uintN		i;
deuce's avatar
deuce committed
828
829
    char*		str=NULL;
    size_t		str_sz=0;
830
831
	size_t		len;
	sbbs_t*		sbbs;
832
	jsrefcount	rc;
833

834
835
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

836
837
838
839
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
840
		JSVALUE_TO_RASTRING(cx, argv[i], str, &str_sz, &len);
841
		if(str==NULL)
842
		    return(JS_FALSE);
843
		rc=JS_SUSPENDREQUEST(cx);
844
		sbbs->putcom(str, len);
845
		JS_RESUMEREQUEST(cx, rc);
846
847
848
849
850
	}

    return(JS_TRUE);
}

851
static JSBool
852
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
853
854
{
	sbbs_t*		sbbs;
855
	jsrefcount	rc;
856

857
858
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

859
860
861
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

862
	js_write(cx,argc,arglist);
863
	rc=JS_SUSPENDREQUEST(cx);
864
865
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);
866
	JS_RESUMEREQUEST(cx, rc);
867
868
869
870
871

    return(JS_TRUE);
}

static JSBool
872
js_printf(JSContext *cx, uintN argc, jsval *arglist)
873
{
874
	jsval *argv=JS_ARGV(cx, arglist);
875
876
	char*		p;
	sbbs_t*		sbbs;
877
	jsrefcount	rc;
878

879
880
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

881
882
883
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

884
885
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
886
887
888
		return(JS_FALSE);
	}

889
	rc=JS_SUSPENDREQUEST(cx);
890
	if(sbbs->online==ON_LOCAL)
891
		eprintf(LOG_INFO,"%s",p);
892
893
	else
		sbbs->bputs(p);
894
	JS_RESUMEREQUEST(cx, rc);
895

896
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
897

898
	js_sprintf_free(p);
899
900
901
902
903

    return(JS_TRUE);
}

static JSBool
904
js_alert(JSContext *cx, uintN argc, jsval *arglist)
905
{
906
	jsval *argv=JS_ARGV(cx, arglist);
907
	sbbs_t*		sbbs;
908
	jsrefcount	rc;
deuce's avatar
deuce committed
909
	char		*cstr;
910

911
912
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

913
914
915
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
916
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
917
	if(cstr==NULL)
918
919
	    return(JS_FALSE);

920
	rc=JS_SUSPENDREQUEST(cx);
921
	sbbs->attr(sbbs->cfg.color[clr_err]);
deuce's avatar
deuce committed
922
	sbbs->bputs(cstr);
deuce's avatar
deuce committed
923
	free(cstr);
924
925
	sbbs->attr(LIGHTGRAY);
	sbbs->bputs(crlf);
926
	JS_RESUMEREQUEST(cx, rc);
927

deuce's avatar
deuce committed
928
929
	JS_SET_RVAL(cx, arglist, argv[0]);

930
931
932
933
    return(JS_TRUE);
}

static JSBool
934
js_confirm(JSContext *cx, uintN argc, jsval *arglist)
935
{
936
	jsval *argv=JS_ARGV(cx, arglist);
937
	sbbs_t*		sbbs;
938
	jsrefcount	rc;
deuce's avatar
deuce committed
939
	char		*cstr;
940

941
942
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

943
944
945
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
946
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
947
	if(cstr==NULL)
948
949
	    return(JS_FALSE);

950
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
951
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->yesno(cstr)));
deuce's avatar
deuce committed
952
	free(cstr);
953
	JS_RESUMEREQUEST(cx, rc);
954
955
956
	return(JS_TRUE);
}

957
static JSBool
958
js_deny(JSContext *cx, uintN argc, jsval *arglist)
959
{
960
	jsval *argv=JS_ARGV(cx, arglist);
961
962
	sbbs_t*		sbbs;
	jsrefcount	rc;
deuce's avatar
deuce committed
963
	char		*cstr;
964

965
966
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

967
968
969
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
970
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
971
	if(cstr==NULL)
972
973
974
	    return(JS_FALSE);

	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
975
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->noyes(cstr)));
deuce's avatar
deuce committed
976
	free(cstr);
977
978
979
980
981
	JS_RESUMEREQUEST(cx, rc);
	return(JS_TRUE);
}


982
static JSBool
983
js_prompt(JSContext *cx, uintN argc, jsval *arglist)
984
{
985
	jsval *argv=JS_ARGV(cx, arglist);
986
987
988
	char		instr[81];
    JSString *	str;
	sbbs_t*		sbbs;
989
	jsrefcount	rc;
990
    char*		prompt=NULL;
991

992
993
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

994
995
996
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

997
998
999
1000
1001
	if(argc) {
		JSVALUE_TO_MSTRING(cx, argv[0], prompt, NULL);
		if(prompt==NULL)
			return(JS_FALSE);
	}
1002
1003

	if(argc>1) {
deuce's avatar
deuce committed
1004
		JSVALUE_TO_STRBUF(cx, argv[1], instr, sizeof(instr), NULL);
1005
1006
1007
	} else
		instr[0]=0;

1008
	rc=JS_SUSPENDREQUEST(cx);
1009
1010
1011
1012
	if(prompt != NULL) {
		sbbs->bprintf("\1n\1y\1h%s\1w: ",prompt);
		free(prompt);
	}
1013
1014

	if(!sbbs->getstr(instr,sizeof(instr)-1,K_EDIT)) {
1015
		JS_SET_RVAL(cx, arglist, JSVAL_NULL);
1016
		JS_RESUMEREQUEST(cx, rc);
1017
1018
		return(JS_TRUE);
	}