main.cpp 159 KB
Newer Older
1
2
/* main.cpp */

3
/* Synchronet terminal server thread and related functions */
4
5
6
7
8
9
10

/* $Id$ */

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

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

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

55
#define TELNET_SERVER "Synchronet Terminal Server"
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#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

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
102
#ifdef _THREAD_SUID_BROKEN
int	thread_suid_broken=TRUE;			/* NPTL is no longer broken */
#endif
103
104
105
106
107

extern "C" {

static bbs_startup_t* startup=NULL;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

deuce's avatar
deuce committed
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
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);
}

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

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

	return(sock);
}

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

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

	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);
257
	if(startup!=NULL && startup->socket_open!=NULL)
258
		startup->socket_open(startup->cbdata,FALSE);
259
	if(result!=0 && ERROR_VALUE!=ENOTSOCK)
260
		lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
261
262
263
	return(result);
}

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

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

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

} /* extern "C" */

285
286
287
288
289
290
291
292
293
294
295
#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) {
296
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
297
298
299
300
		WSAInitialized=TRUE;
		return(TRUE);
	}

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

#else /* No WINSOCK */

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

#endif

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

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

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

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

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

337
338
#ifdef JAVASCRIPT

339
340
static js_server_props_t js_server_props;

341
342
343
344
345
346
347
348
349
350
351
352
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
353
354
		if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)	/* Assertion here, in _heap_alloc_dbg, June-21-2004 */
			return(JS_FALSE);								/* Caused by nntpservice.js? */
355

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

360
361
362
363
364
365
366
367
368
369
370
	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;
	}

371
	return(JS_TRUE);
372
373
}

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

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

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

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

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

JSBool
394
DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
395
396
397
398
399
400
401
402
403
404
{
	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));
}

405
#ifdef BUILD_JSDOCS
406
407

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

/*
 * 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
422
423
	"null",
	"xml",
424
425
	"array",
	"alias",
deuce's avatar
deuce committed
426
	"undefined"
427
428
};

429
430
431
432
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint		i;
433
	long		ver;
434
435
436
437
438
439
440
441
442
443
444
445
	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++) {
446
447
		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))
448
449
			return(JS_FALSE);
		if(props[i].flags&JSPROP_ENUMERATE) {	/* No need to version invisible props */
450
451
452
			if((ver=props[i].ver) < 10000)		/* auto convert 313 to 31300 */
				ver*=100;
			val = INT_TO_JSVAL(ver);
453
454
455
456
457
458
459
460
			if(!JS_SetElement(cx, array, len++, &val))
				return(JS_FALSE);
		}
	}

	return(JS_TRUE);
}

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

	/* Return existing method_list array if it's already been created */
deuce's avatar
deuce committed
475
	if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID) {
476
		method_array=JSVAL_TO_OBJECT(val);
deuce's avatar
deuce committed
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
		// 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 {
492
493
		if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL) 
			return(JS_FALSE);
deuce's avatar
deuce committed
494
495
		if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
				, NULL, NULL, 0))
496
			return(JS_FALSE);
deuce's avatar
deuce committed
497
	}
498
499
500

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

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

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

507
		method = JS_NewObject(cx, NULL, NULL, method_array);	/* exception here June-7-2003 */
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
541

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

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

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

	return(JS_TRUE);
}

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

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

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

582
583
584
	return(ret);
}

585
#else // NON-JSDOCS
586

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

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

	return(JS_TRUE);
}


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

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

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

	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);
			}
		}
	}
639
	if(consts) {
deuce's avatar
deuce committed
640
		for(i=0;consts[i].name;i++) {
641
			if(name==NULL || strcmp(name, consts[i].name)==0) {
642
				val=INT_TO_JSVAL(consts[i].val);
643

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

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

	return(JS_TRUE);
}

656
657
#endif

658
659
660
661
662
663
664
665
/* 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++) {
666
		val=INT_TO_JSVAL(ints[i].val);
667
668
669
670

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

672
673
674
	return(JS_TRUE);
}

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

687
688
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

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

730
731
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

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

	free(buf);
	return(JS_TRUE);
}

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

763
764
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

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

	free(buf);
	return(JS_TRUE);
}

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

798
799
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

835
836
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

    return(JS_TRUE);
}

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

858
859
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

    return(JS_TRUE);
}

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

880
881
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

899
	js_sprintf_free(p);
900
901
902
903
904

    return(JS_TRUE);
}

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

912
913
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

931
932
933
934
    return(JS_TRUE);
}

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

942
943
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

966
967
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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


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

993
994
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

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