main.cpp 162 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
#define STATUS_WFC	"Listening"

#define TIMEOUT_THREAD_WAIT		60			// Seconds (was 15)
#define IO_THREAD_BUF_SIZE	   	20000		// Bytes
59
#define TIMEOUT_MUTEX_FILE		12*60*60
60
61
62
63
64
65
66
67
68
69
70
71
72

// 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

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

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

82
static	protected_uint32_t node_threads_running;
83
84
85
86
87
88
89
90
		
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
91
struct xpms_set				*ts_set;
92
93
94
95
96
static	sbbs_t*	sbbs=NULL;
static	scfg_t	scfg;
static	char *	text[TOTAL_TEXT];
static	WORD	first_node;
static	WORD	last_node;
97
static	bool	terminate_server=false;
98
99
static	str_list_t recycle_semfiles;
static	str_list_t shutdown_semfiles;
100
101
static	link_list_t current_logins;
static	link_list_t current_connections;
102
103
104
#ifdef _THREAD_SUID_BROKEN
int	thread_suid_broken=TRUE;			/* NPTL is no longer broken */
#endif
105
106
107
108
109

extern "C" {

static bbs_startup_t* startup=NULL;

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

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

void client_on(SOCKET sock, client_t* client, BOOL update)
{
125
126
	if(!update)
		listAddNodeData(&current_connections, client->addr, strlen(client->addr)+1, sock, LAST_NODE);
127
	if(startup!=NULL && startup->client_on!=NULL)
128
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
129
130
131
132
}

static void client_off(SOCKET sock)
{
133
	listRemoveTaggedNode(&current_connections, sock, /* free_data */TRUE);
134
	if(startup!=NULL && startup->client_on!=NULL)
135
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
136
137
138
139
140
}

static void thread_up(BOOL setuid)
{
	if(startup!=NULL && startup->thread_up!=NULL)
141
		startup->thread_up(startup->cbdata,TRUE,setuid);
142
143
144
145
146
}

static void thread_down()
{
	if(startup!=NULL && startup->thread_up!=NULL)
147
		startup->thread_up(startup->cbdata,FALSE,FALSE);
148
149
}

150
int lputs(int level, const char* str)
151
{
152
	if(level <= LOG_ERR) {
153
		errorlog(&scfg,startup==NULL ? NULL:startup->host_name, str);
154
155
156
		if(startup!=NULL && startup->errormsg!=NULL)
			startup->errormsg(startup->cbdata,level,str);
	}
157

158
	if(startup==NULL || startup->lputs==NULL || str==NULL || level > startup->log_level)
159
160
    	return(0);

161
162
163
164
165
#if defined(_WIN32)
	if(IsBadCodePtr((FARPROC)startup->lputs))
		return(0);
#endif

166
    return(startup->lputs(startup->cbdata,level,str));
167
168
}

169
170
171
172
173
174
175
176
177
178
179
180
181
182
int eputs(int level, const char *str)
{
	if(level <= LOG_ERR) {
		errorlog(&scfg,startup==NULL ? NULL:startup->host_name, str);
		if(startup!=NULL && startup->errormsg!=NULL)
			startup->errormsg(startup->cbdata,level,str);
	}

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

    return(startup->event_lputs(startup->event_cbdata,level,str));
}

183
int lprintf(int level, const char *fmt, ...)
184
185
186
187
188
189
190
191
{
	va_list argptr;
	char sbuf[1024];

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
192
    return(lputs(level,sbuf));
193
194
}

195
int eprintf(int level, const char *fmt, ...)
196
197
198
199
200
201
202
203
{
	va_list argptr;
	char sbuf[1024];

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
204

205
	strip_ctrl(sbuf, sbuf);
206

207
208
    return(eputs(level,sbuf));
}
209

210
211
212
213
214
215
/* Picks the right log callback function (event or term) based on the sbbs->cfg.node_num value */
int sbbs_t::lputs(int level, const char* str)
{
	if(cfg.node_num == 0)
		return ::eputs(level, str);
	return ::lputs(level, str);
216
217
}

deuce's avatar
deuce committed
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
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);
}

242
SOCKET open_socket(int type, const char* protocol)
243
244
245
246
247
248
{
	SOCKET	sock;
	char	error[256];

	sock=socket(AF_INET, type, IPPROTO_IP);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
249
		startup->socket_open(startup->cbdata,TRUE);
250
	if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
251
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
252
253
254
255

	return(sock);
}

deuce's avatar
deuce committed
256
SOCKET accept_socket(SOCKET s, union xp_sockaddr* addr, socklen_t* addrlen)
257
258
259
{
	SOCKET	sock;

deuce's avatar
deuce committed
260
261
	sock=accept(s,&addr->addr,addrlen);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
262
		startup->socket_open(startup->cbdata,TRUE);
263
264
265
266
267
268
269
270
271
272
273
274
275

	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);
276
	if(startup!=NULL && startup->socket_open!=NULL)
277
		startup->socket_open(startup->cbdata,FALSE);
278
	if(result!=0 && ERROR_VALUE!=ENOTSOCK)
279
		lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
280
281
282
	return(result);
}

deuce's avatar
deuce committed
283
/* TODO: IPv6 */
284
285
286
287
288
289
u_long resolve_ip(char *addr)
{
	HOSTENT*	host;
	char*		p;

	if(*addr==0)
290
		return((u_long)INADDR_NONE);
291
292

	for(p=addr;*p;p++)
293
		if(*p!='.' && !isdigit((uchar)*p))
294
295
296
297
			break;
	if(!(*p))
		return(inet_addr(addr));
	if((host=gethostbyname(addr))==NULL) 
298
		return((u_long)INADDR_NONE);
299
300
301
302
303
	return(*((ulong*)host->h_addr_list[0]));
}

} /* extern "C" */

304
305
306
307
308
309
310
311
312
313
314
#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) {
315
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
316
317
318
319
		WSAInitialized=TRUE;
		return(TRUE);
	}

320
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
321
322
323
324
325
326
327
328
329
330
	return(FALSE);
}

#else /* No WINSOCK */

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

#endif

331
332
DLLEXPORT void DLLCALL sbbs_srand()
{
333
	DWORD seed;
334

335
	xp_randomize();
336
#if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
337
	int     rf,rd=0;
338

339
340
	if((rf=open(RANDOM_DEV, O_RDONLY|O_NONBLOCK))!=-1) {
		rd=read(rf, &seed, sizeof(seed));
341
342
		close(rf);
	}
deuce's avatar
deuce committed
343
	if (rd != sizeof(seed))
344
#endif
345
		seed = time32(NULL) ^ (uintmax_t)GetCurrentThreadId();
346
347

 	srand(seed);
348
349
350
	sbbs_random(10);	/* Throw away first number */
}

351
int DLLCALL sbbs_random(int n)
352
353
354
355
{
	return(xp_random(n));
}

356
357
#ifdef JAVASCRIPT

358
359
static js_server_props_t js_server_props;

360
361
362
363
364
365
366
367
368
369
370
371
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
372
373
		if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)	/* Assertion here, in _heap_alloc_dbg, June-21-2004 */
			return(JS_FALSE);								/* Caused by nntpservice.js? */
374

375
376
377
378
	if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
		,NULL,NULL,flags))
		return(JS_FALSE);

379
380
381
382
383
384
385
386
387
388
389
	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;
	}

390
	return(JS_TRUE);
391
392
}

393
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
394
395

JSBool
396
DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
397
398
399
400
401
402
{
	JSString* js_str = JS_NewStringCopyZ(cx, str);

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

rswindell's avatar
rswindell committed
403
404
405
	if(ver < 10000)		/* auto convert 313 to 31300 */
		ver*=100;

406
	return(JS_DefineProperty(cx,obj,"_description"
407
408
409
			,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
		&& JS_DefineProperty(cx,obj,"_ver"
			,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
410
411
412
}

JSBool
413
DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
414
415
416
417
418
419
420
421
422
423
{
	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));
}

424
#ifdef BUILD_JSDOCS
425
426

static const char* method_array_name = "_method_list";
427
static const char* propver_array_name = "_property_ver_list";
428
429
430
431
432
433
434
435
436
437
438
439
440

/*
 * 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
441
442
	"null",
	"xml",
443
444
	"array",
	"alias",
deuce's avatar
deuce committed
445
	"undefined"
446
447
};

448
449
450
451
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint		i;
452
	long		ver;
453
454
455
456
457
458
459
460
461
462
463
464
	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++) {
465
466
		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))
467
468
			return(JS_FALSE);
		if(props[i].flags&JSPROP_ENUMERATE) {	/* No need to version invisible props */
469
470
471
			if((ver=props[i].ver) < 10000)		/* auto convert 313 to 31300 */
				ver*=100;
			val = INT_TO_JSVAL(ver);
472
473
474
475
476
477
478
479
			if(!JS_SetElement(cx, array, len++, &val))
				return(JS_FALSE);
		}
	}

	return(JS_TRUE);
}

480
JSBool 
deuce's avatar
deuce committed
481
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
482
483
484
{
	int			i;
	jsuint		len=0;
485
	long		ver;
486
487
488
489
	jsval		val;
	JSObject*	method;
	JSObject*	method_array;
	JSString*	js_str;
deuce's avatar
deuce committed
490
491
	size_t		str_len=0;
	char		*str=NULL;
492
493

	/* Return existing method_list array if it's already been created */
deuce's avatar
deuce committed
494
	if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID) {
495
		method_array=JSVAL_TO_OBJECT(val);
deuce's avatar
deuce committed
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
		// 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 {
511
512
		if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL) 
			return(JS_FALSE);
deuce's avatar
deuce committed
513
514
		if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
				, NULL, NULL, 0))
515
			return(JS_FALSE);
deuce's avatar
deuce committed
516
	}
517
518
519

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

520
521
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
522
523
524
525

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

526
		method = JS_NewObject(cx, NULL, NULL, method_array);	/* exception here June-7-2003 */
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560

		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);
		}

561
		if(funcs[i].ver) {
562
			if((ver=funcs[i].ver) < 10000)		/* auto convert 313 to 31300 */
563
564
				ver*=100;
			val = INT_TO_JSVAL(ver);
565
566
567
			JS_SetProperty(cx,method, "ver", &val);
		}

568
569
570
571
572
573
574
575
		val=OBJECT_TO_JSVAL(method);
		if(!JS_SetElement(cx, method_array, len+i, &val))
			return(JS_FALSE);
	}

	return(JS_TRUE);
}

576
577
578
579
580
/*
 * Always resolve all here since
 * 1) We'll always be enumerating anyways
 * 2) The speed penalty won't be seen in production code anyways
 */
581
582
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
583
584
585
{
	JSBool	ret=JS_TRUE;

deuce's avatar
deuce committed
586
	if(props) {
587
588
		if(!js_DefineSyncProperties(cx, obj, props))
			ret=JS_FALSE;
deuce's avatar
deuce committed
589
	}
590
		
deuce's avatar
deuce committed
591
	if(funcs) {
deuce's avatar
deuce committed
592
		if(!js_DefineSyncMethods(cx, obj, funcs))
593
			ret=JS_FALSE;
deuce's avatar
deuce committed
594
	}
595

deuce's avatar
deuce committed
596
597
598
599
	if(consts) {
		if(!js_DefineConstIntegers(cx, obj, consts, flags))
			ret=JS_FALSE;
	}
600

601
602
603
	return(ret);
}

604
#else // NON-JSDOCS
605

606
607
608
609
610
611
612
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint i;

	for(i=0;props[i].name;i++) 
		if(!JS_DefinePropertyWithTinyId(cx, obj, 
613
			props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
614
615
616
617
618
619
			return(JS_FALSE);

	return(JS_TRUE);
}


620
JSBool 
deuce's avatar
deuce committed
621
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
622
{
623
	uint i;
624
625

	for(i=0;funcs[i].name;i++)
626
627
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
628
629
630
	return(JS_TRUE);
}

631
632
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
633
634
{
	uint i;
635
	jsval	val;
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657

	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);
			}
		}
	}
658
	if(consts) {
deuce's avatar
deuce committed
659
		for(i=0;consts[i].name;i++) {
660
			if(name==NULL || strcmp(name, consts[i].name)==0) {
661
				val=INT_TO_JSVAL(consts[i].val);
662

deuce's avatar
deuce committed
663
				if(!JS_DefineProperty(cx, obj, consts[i].name, val ,NULL, NULL, flags))
664
665
666
667
668
669
670
					return(JS_FALSE);

				if(name)
					return(JS_TRUE);
			}
		}
	}
671
672
673
674

	return(JS_TRUE);
}

675
676
#endif

677
678
679
680
681
682
683
684
/* 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++) {
685
		val=INT_TO_JSVAL(ints[i].val);
686
687
688
689

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

691
692
693
	return(JS_TRUE);
}

694
static JSBool
695
js_log(JSContext *cx, uintN argc, jsval *arglist)
696
{
697
	jsval *argv=JS_ARGV(cx, arglist);
698
699
    uintN		i=0;
	int32		level=LOG_INFO;
700
    JSString*	str=NULL;
701
	sbbs_t*		sbbs;
702
	jsrefcount	rc;
deuce's avatar
deuce committed
703
704
	char		*line=NULL;
	size_t		line_sz=0;
705

706
707
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

708
709
710
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

711
712
713
714
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
715
716

    for(; i<argc; i++) {
deuce's avatar
deuce committed
717
718
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			return(JS_FALSE);
deuce's avatar
deuce committed
719
		JSSTRING_TO_RASTRING(cx, str, line, &line_sz, NULL);
deuce's avatar
deuce committed
720
		if(line==NULL)
721
		    return(JS_FALSE);
722
		rc=JS_SUSPENDREQUEST(cx);
723
		if(sbbs->online==ON_LOCAL) {
deuce's avatar
deuce committed
724
725
726
			if(startup!=NULL && startup->event_lputs!=NULL && level <= startup->log_level) {
				startup->event_lputs(startup->event_cbdata,level,line);
			}
727
		} else
deuce's avatar
deuce committed
728
			lprintf(level,"Node %d %s", sbbs->cfg.node_num, line);
729
		JS_RESUMEREQUEST(cx, rc);
730
	}
deuce's avatar
deuce committed
731
	free(line);
732

733
	if(str==NULL)
734
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
735
	else
736
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
737
738
739
    return(JS_TRUE);
}

740
static JSBool
741
js_read(JSContext *cx, uintN argc, jsval *arglist)
742
{
743
	jsval *argv=JS_ARGV(cx, arglist);
744
745
746
	uchar*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
747
	jsrefcount	rc;
748

749
750
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

751
752
753
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

754
755
756
757
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
758
759
760
761

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

762
	rc=JS_SUSPENDREQUEST(cx);
763
	len=RingBufRead(&sbbs->inbuf,buf,len);
764
	JS_RESUMEREQUEST(cx, rc);
765
766

	if(len>0)
767
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len)));
768
769
770
771
772
773

	free(buf);
	return(JS_TRUE);
}

static JSBool
774
js_readln(JSContext *cx, uintN argc, jsval *arglist)
775
{
776
	jsval *argv=JS_ARGV(cx, arglist);
777
778
779
	char*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
780
	jsrefcount	rc;
781

782
783
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

784
785
786
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

787
788
789
790
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
791
792
793
794

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

795
	rc=JS_SUSPENDREQUEST(cx);
796
	len=sbbs->getstr(buf,len,K_NONE);
797
	JS_RESUMEREQUEST(cx, rc);
798
799

	if(len>0)
800
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf)));
801
802
803
804
805

	free(buf);
	return(JS_TRUE);
}

806
static JSBool
807
js_write(JSContext *cx, uintN argc, jsval *arglist)
808
{
809
	jsval *argv=JS_ARGV(cx, arglist);
810
    uintN		i;
811
    JSString*	str=NULL;
812
	sbbs_t*		sbbs;
813
	jsrefcount	rc;
814
815
	char		*cstr=NULL;
	size_t		cstr_sz=0;
816

817
818
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

819
820
821
822
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
823
824
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			return(JS_FALSE);
deuce's avatar
deuce committed
825
		JSSTRING_TO_RASTRING(cx, str, cstr, &cstr_sz, NULL);
deuce's avatar
deuce committed
826
		if(cstr==NULL)
827
		    return(JS_FALSE);
828
		rc=JS_SUSPENDREQUEST(cx);
829
		if(sbbs->online==ON_LOCAL)
deuce's avatar
deuce committed
830
			eprintf(LOG_INFO,"%s",cstr);
831
		else
deuce's avatar
deuce committed
832
			sbbs->bputs(cstr);
833
		JS_RESUMEREQUEST(cx, rc);
834
835
836
	}

	if(str==NULL)
837
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
838
	else
839
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
840
841
842
    return(JS_TRUE);
}

843
static JSBool
844
js_write_raw(JSContext *cx, uintN argc, jsval *arglist)
845
{
846
	jsval *argv=JS_ARGV(cx, arglist);
847
    uintN		i;
deuce's avatar
deuce committed
848
849
    char*		str=NULL;
    size_t		str_sz=0;
850
851
	size_t		len;
	sbbs_t*		sbbs;
852
	jsrefcount	rc;
853

854
855
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

856
857
858
859
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
860
		JSVALUE_TO_RASTRING(cx, argv[i], str, &str_sz, &len);
861
		if(str==NULL)
862
		    return(JS_FALSE);
863
		rc=JS_SUSPENDREQUEST(cx);
864
		sbbs->putcom(str, len);
865
		JS_RESUMEREQUEST(cx, rc);
866
867
868
869
870
	}

    return(JS_TRUE);
}

871
static JSBool
872
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
873
874
{
	sbbs_t*		sbbs;
875
	jsrefcount	rc;
876

877
878
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

879
880
881
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

882
	js_write(cx,argc,arglist);
883
	rc=JS_SUSPENDREQUEST(cx);
884
885
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);
886
	JS_RESUMEREQUEST(cx, rc);
887
888
889
890
891

    return(JS_TRUE);
}

static JSBool
892
js_printf(JSContext *cx, uintN argc, jsval *arglist)
893
{
894
	jsval *argv=JS_ARGV(cx, arglist);
895
896
	char*		p;
	sbbs_t*		sbbs;
897
	jsrefcount	rc;
898

899
900
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

901
902
903
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

904
905
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
906
907
908
		return(JS_FALSE);
	}

909
	rc=JS_SUSPENDREQUEST(cx);
910
	if(sbbs->online==ON_LOCAL)
911
		eprintf(LOG_INFO,"%s",p);
912
913
	else
		sbbs->bputs(p);
914
	JS_RESUMEREQUEST(cx, rc);
915

916
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
917

918
	js_sprintf_free(p);
919
920
921
922
923

    return(JS_TRUE);
}

static JSBool
924
js_alert(JSContext *cx, uintN argc, jsval *arglist)
925
{
926
	jsval *argv=JS_ARGV(cx, arglist);
927
	sbbs_t*		sbbs;
928
	jsrefcount	rc;
deuce's avatar
deuce committed
929
	char		*cstr;
930

931
932
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

933
934
935
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
936
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
937
	if(cstr==NULL)
938
939
	    return(JS_FALSE);

940
	rc=JS_SUSPENDREQUEST(cx);
941
	sbbs->attr(sbbs->cfg.color[clr_err]);
deuce's avatar
deuce committed
942
	sbbs->bputs(cstr);
deuce's avatar
deuce committed
943
	free(cstr);
944
945
	sbbs->attr(LIGHTGRAY);
	sbbs->bputs(crlf);
946
	JS_RESUMEREQUEST(cx, rc);
947

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

950
951
952
953
    return(JS_TRUE);
}

static JSBool
954
js_confirm(JSContext *cx, uintN argc, jsval *arglist)
955
{
956
	jsval *argv=JS_ARGV(cx, arglist);
957
	sbbs_t*		sbbs;
958
	jsrefcount	rc;
deuce's avatar
deuce committed
959
	char		*cstr;
960

961
962
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

963
964
965
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
966
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
967
	if(cstr==NULL)
968
969
	    return(JS_FALSE);

970
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
971
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->yesno(cstr)));
deuce's avatar
deuce committed
972
	free(cstr);
973
	JS_RESUMEREQUEST(cx, rc);
974
975
976
	return(JS_TRUE);
}

977
static JSBool
978
js_deny(JSContext *cx, uintN argc, jsval *arglist)
979
{
980
	jsval *argv=JS_ARGV(cx, arglist);
981
982
	sbbs_t*		sbbs;
	jsrefcount	rc;
deuce's avatar
deuce committed
983
	char		*cstr;
984

985
986
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

987
988
989
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
990
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
991
	if(cstr==NULL)
992
993
994
	    return(JS_FALSE);

	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
995
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->noyes(cstr)));
deuce's avatar
deuce committed
996
	free(cstr);
997
998
999
1000
1001
	JS_RESUMEREQUEST(cx, rc);
	return(JS_TRUE);
}


1002
static JSBool
1003
js_prompt(JSContext *cx, uintN argc, jsval *arglist)
1004
{
1005
	jsval *argv=JS_ARGV(cx, arglist);
1006
1007
1008
	char		instr[81];
    JSString *	str;
	sbbs_t*		sbbs;
1009
	jsrefcount	rc;
1010
    char*		prompt=NULL;
1011

1012
1013
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

1014
1015
1016