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 2013 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 "js_socket.h"
45
46
47

#ifdef __unix__
	#include <sys/un.h>
deuce's avatar
deuce committed
48
49
50
51
	#ifndef SUN_LEN
		#define SUN_LEN(su) \
	        (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
	#endif
52
53
54
55
#endif

//---------------------------------------------------------------------------

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

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

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

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

extern "C" {

static bbs_startup_t* startup=NULL;

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

static void update_clients()
{
	if(startup!=NULL && startup->clients!=NULL)
123
		startup->clients(startup->cbdata,node_threads_running.value);
124
125
126
127
128
}

void client_on(SOCKET sock, client_t* client, BOOL update)
{
	if(startup!=NULL && startup->client_on!=NULL)
129
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
130
131
132
133
134
}

static void client_off(SOCKET sock)
{
	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
int lprintf(int level, const char *fmt, ...)
170
171
172
173
174
175
176
177
{
	va_list argptr;
	char sbuf[1024];

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
178
    return(lputs(level,sbuf));
179
180
}

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

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
190
191
192
193
194
195
196
197
198
199

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

200
	strip_ctrl(sbuf, sbuf);
201
    return(startup->event_lputs(startup->event_cbdata,level,sbuf));
202
203
}

204
SOCKET open_socket(int type, const char* protocol)
205
206
207
208
209
210
{
	SOCKET	sock;
	char	error[256];

	sock=socket(AF_INET, type, IPPROTO_IP);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
211
		startup->socket_open(startup->cbdata,TRUE);
212
	if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
213
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
214
215
216
217
218
219
220
221
222
223

	return(sock);
}

SOCKET accept_socket(SOCKET s, SOCKADDR* addr, socklen_t* addrlen)
{
	SOCKET	sock;

	sock=accept(s,addr,addrlen);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
224
		startup->socket_open(startup->cbdata,TRUE);
225
226
227
228
229
230
231
232
233
234
235
236
237

	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);
238
	if(startup!=NULL && startup->socket_open!=NULL)
239
		startup->socket_open(startup->cbdata,FALSE);
240
	if(result!=0 && ERROR_VALUE!=ENOTSOCK)
241
		lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
242
243
244
245
246
247
248
249
250
251
	return(result);
}


u_long resolve_ip(char *addr)
{
	HOSTENT*	host;
	char*		p;

	if(*addr==0)
252
		return((u_long)INADDR_NONE);
253
254

	for(p=addr;*p;p++)
255
		if(*p!='.' && !isdigit((uchar)*p))
256
257
258
259
			break;
	if(!(*p))
		return(inet_addr(addr));
	if((host=gethostbyname(addr))==NULL) 
260
		return((u_long)INADDR_NONE);
261
262
263
264
265
	return(*((ulong*)host->h_addr_list[0]));
}

} /* extern "C" */

266
267
268
269
270
271
272
273
274
275
276
#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) {
277
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
278
279
280
281
		WSAInitialized=TRUE;
		return(TRUE);
	}

282
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
283
284
285
286
287
288
289
290
291
292
	return(FALSE);
}

#else /* No WINSOCK */

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

#endif

293
294
DLLEXPORT void DLLCALL sbbs_srand()
{
295
	DWORD seed;
296

297
	xp_randomize();
298
299
300
301
302
303
304
#if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
	int     rf;

	if((rf=open(RANDOM_DEV, O_RDONLY))!=-1) {
		read(rf, &seed, sizeof(seed));
		close(rf);
	}
305
#else
306
	seed = time32(NULL) ^ (DWORD)GetCurrentThreadId();
307
308
309
#endif

 	srand(seed);
310
311
312
	sbbs_random(10);	/* Throw away first number */
}

313
int DLLCALL sbbs_random(int n)
314
315
316
317
{
	return(xp_random(n));
}

318
319
#ifdef JAVASCRIPT

320
321
static js_server_props_t js_server_props;

322
323
324
325
326
327
328
329
330
331
332
333
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
334
335
		if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)	/* Assertion here, in _heap_alloc_dbg, June-21-2004 */
			return(JS_FALSE);								/* Caused by nntpservice.js? */
336

337
338
339
340
	if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
		,NULL,NULL,flags))
		return(JS_FALSE);

341
342
343
344
345
346
347
348
349
350
351
	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;
	}

352
	return(JS_TRUE);
353
354
}

355
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
356
357

JSBool
358
DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
359
360
361
362
363
364
{
	JSString* js_str = JS_NewStringCopyZ(cx, str);

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

rswindell's avatar
rswindell committed
365
366
367
	if(ver < 10000)		/* auto convert 313 to 31300 */
		ver*=100;

368
	return(JS_DefineProperty(cx,obj,"_description"
369
370
371
			,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
		&& JS_DefineProperty(cx,obj,"_ver"
			,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
372
373
374
}

JSBool
375
DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
376
377
378
379
380
381
382
383
384
385
{
	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));
}

386
#ifdef BUILD_JSDOCS
387
388

static const char* method_array_name = "_method_list";
389
static const char* propver_array_name = "_property_ver_list";
390
391
392
393
394
395
396
397
398
399
400
401
402

/*
 * 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
403
404
	"null",
	"xml",
405
406
	"array",
	"alias",
deuce's avatar
deuce committed
407
	"undefined"
408
409
};

410
411
412
413
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint		i;
414
	long		ver;
415
416
417
418
419
420
421
422
423
424
425
426
	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++) {
427
428
		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))
429
430
			return(JS_FALSE);
		if(props[i].flags&JSPROP_ENUMERATE) {	/* No need to version invisible props */
431
432
433
			if((ver=props[i].ver) < 10000)		/* auto convert 313 to 31300 */
				ver*=100;
			val = INT_TO_JSVAL(ver);
434
435
436
437
438
439
440
441
			if(!JS_SetElement(cx, array, len++, &val))
				return(JS_FALSE);
		}
	}

	return(JS_TRUE);
}

442
JSBool 
443
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
444
445
446
{
	int			i;
	jsuint		len=0;
447
	long		ver;
448
449
450
451
452
453
454
455
456
457
458
459
	jsval		val;
	JSObject*	method;
	JSObject*	method_array;
	JSString*	js_str;

	/* Return existing method_list array if it's already been created */
	if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID)
		method_array=JSVAL_TO_OBJECT(val);
	else
		if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL) 
			return(JS_FALSE);

460
461
462
463
	if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
		, NULL, NULL, 0))
		return(JS_FALSE);

464
465
466
467
468
469
	if(append)
		if(!JS_GetArrayLength(cx, method_array, &len))
			return(JS_FALSE);

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

470
471
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
472
473
474
475

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

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

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

511
		if(funcs[i].ver) {
512
			if((ver=funcs[i].ver) < 10000)		/* auto convert 313 to 31300 */
513
514
				ver*=100;
			val = INT_TO_JSVAL(ver);
515
516
517
			JS_SetProperty(cx,method, "ver", &val);
		}

518
519
520
521
522
523
524
525
		val=OBJECT_TO_JSVAL(method);
		if(!JS_SetElement(cx, method_array, len+i, &val))
			return(JS_FALSE);
	}

	return(JS_TRUE);
}

526
527
528
529
530
/*
 * Always resolve all here since
 * 1) We'll always be enumerating anyways
 * 2) The speed penalty won't be seen in production code anyways
 */
531
532
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
533
534
535
{
	JSBool	ret=JS_TRUE;

deuce's avatar
deuce committed
536
	if(props) {
537
538
		if(!js_DefineSyncProperties(cx, obj, props))
			ret=JS_FALSE;
deuce's avatar
deuce committed
539
	}
540
		
deuce's avatar
deuce committed
541
542
	if(funcs) {
		if(!js_DefineSyncMethods(cx, obj, funcs, 0))
543
			ret=JS_FALSE;
deuce's avatar
deuce committed
544
	}
545

deuce's avatar
deuce committed
546
547
548
549
	if(consts) {
		if(!js_DefineConstIntegers(cx, obj, consts, flags))
			ret=JS_FALSE;
	}
550

551
552
553
	return(ret);
}

554
#else // NON-JSDOCS
555

556
557
558
559
560
561
562
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint i;

	for(i=0;props[i].name;i++) 
		if(!JS_DefinePropertyWithTinyId(cx, obj, 
563
			props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
564
565
566
567
568
569
			return(JS_FALSE);

	return(JS_TRUE);
}


570
JSBool 
571
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
572
{
573
	uint i;
574
575

	for(i=0;funcs[i].name;i++)
576
577
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
578
579
580
	return(JS_TRUE);
}

581
582
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
583
584
{
	uint i;
585
	jsval	val;
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607

	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);
			}
		}
	}
608
	if(consts) {
deuce's avatar
deuce committed
609
		for(i=0;consts[i].name;i++) {
610
			if(name==NULL || strcmp(name, consts[i].name)==0) {
611
				val=INT_TO_JSVAL(consts[i].val);
612

deuce's avatar
deuce committed
613
				if(!JS_DefineProperty(cx, obj, consts[i].name, val ,NULL, NULL, flags))
614
615
616
617
618
619
620
					return(JS_FALSE);

				if(name)
					return(JS_TRUE);
			}
		}
	}
621
622
623
624

	return(JS_TRUE);
}

625
626
#endif

627
628
629
630
631
632
633
634
/* 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++) {
635
		val=INT_TO_JSVAL(ints[i].val);
636
637
638
639

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

641
642
643
	return(JS_TRUE);
}

644
static JSBool
645
js_log(JSContext *cx, uintN argc, jsval *arglist)
646
{
647
	jsval *argv=JS_ARGV(cx, arglist);
648
649
    uintN		i=0;
	int32		level=LOG_INFO;
650
    JSString*	str=NULL;
651
	sbbs_t*		sbbs;
652
	jsrefcount	rc;
deuce's avatar
deuce committed
653
654
	char		*line=NULL;
	size_t		line_sz=0;
655

656
657
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

658
659
660
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

661
662
663
664
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
665
666

    for(; i<argc; i++) {
deuce's avatar
deuce committed
667
668
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			return(JS_FALSE);
deuce's avatar
deuce committed
669
		JSSTRING_TO_RASTRING(cx, str, line, &line_sz, NULL);
deuce's avatar
deuce committed
670
		if(line==NULL)
671
		    return(JS_FALSE);
672
		rc=JS_SUSPENDREQUEST(cx);
673
		if(sbbs->online==ON_LOCAL) {
deuce's avatar
deuce committed
674
675
676
			if(startup!=NULL && startup->event_lputs!=NULL && level <= startup->log_level) {
				startup->event_lputs(startup->event_cbdata,level,line);
			}
677
		} else
deuce's avatar
deuce committed
678
			lprintf(level,"Node %d %s", sbbs->cfg.node_num, line);
679
		JS_RESUMEREQUEST(cx, rc);
680
	}
deuce's avatar
deuce committed
681
	free(line);
682

683
	if(str==NULL)
684
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
685
	else
686
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
687
688
689
    return(JS_TRUE);
}

690
static JSBool
691
js_read(JSContext *cx, uintN argc, jsval *arglist)
692
{
693
	jsval *argv=JS_ARGV(cx, arglist);
694
695
696
	uchar*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
697
	jsrefcount	rc;
698

699
700
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

701
702
703
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

704
705
706
707
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
708
709
710
711

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

712
	rc=JS_SUSPENDREQUEST(cx);
713
	len=RingBufRead(&sbbs->inbuf,buf,len);
714
	JS_RESUMEREQUEST(cx, rc);
715
716

	if(len>0)
717
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len)));
718
719
720
721
722
723

	free(buf);
	return(JS_TRUE);
}

static JSBool
724
js_readln(JSContext *cx, uintN argc, jsval *arglist)
725
{
726
	jsval *argv=JS_ARGV(cx, arglist);
727
728
729
	char*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
730
	jsrefcount	rc;
731

732
733
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

734
735
736
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

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

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

745
	rc=JS_SUSPENDREQUEST(cx);
746
	len=sbbs->getstr(buf,len,K_NONE);
747
	JS_RESUMEREQUEST(cx, rc);
748
749

	if(len>0)
750
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf)));
751
752
753
754
755

	free(buf);
	return(JS_TRUE);
}

756
static JSBool
757
js_write(JSContext *cx, uintN argc, jsval *arglist)
758
{
759
	jsval *argv=JS_ARGV(cx, arglist);
760
    uintN		i;
761
    JSString*	str=NULL;
762
	sbbs_t*		sbbs;
763
	jsrefcount	rc;
764
765
	char		*cstr=NULL;
	size_t		cstr_sz=0;
766

767
768
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

769
770
771
772
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
773
774
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			return(JS_FALSE);
deuce's avatar
deuce committed
775
		JSSTRING_TO_RASTRING(cx, str, cstr, &cstr_sz, NULL);
deuce's avatar
deuce committed
776
		if(cstr==NULL)
777
		    return(JS_FALSE);
778
		rc=JS_SUSPENDREQUEST(cx);
779
		if(sbbs->online==ON_LOCAL)
deuce's avatar
deuce committed
780
			eprintf(LOG_INFO,"%s",cstr);
781
		else
deuce's avatar
deuce committed
782
			sbbs->bputs(cstr);
783
		JS_RESUMEREQUEST(cx, rc);
784
785
786
	}

	if(str==NULL)
787
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
788
	else
789
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
790
791
792
    return(JS_TRUE);
}

793
static JSBool
794
js_write_raw(JSContext *cx, uintN argc, jsval *arglist)
795
{
796
	jsval *argv=JS_ARGV(cx, arglist);
797
    uintN		i;
deuce's avatar
deuce committed
798
799
    char*		str=NULL;
    size_t		str_sz=0;
800
801
	size_t		len;
	sbbs_t*		sbbs;
802
	jsrefcount	rc;
803

804
805
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

806
807
808
809
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
810
		JSVALUE_TO_RASTRING(cx, argv[i], str, &str_sz, &len);
811
		if(str==NULL)
812
		    return(JS_FALSE);
813
		rc=JS_SUSPENDREQUEST(cx);
814
		sbbs->putcom(str, len);
815
		JS_RESUMEREQUEST(cx, rc);
816
817
818
819
820
	}

    return(JS_TRUE);
}

821
static JSBool
822
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
823
824
{
	sbbs_t*		sbbs;
825
	jsrefcount	rc;
826

827
828
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

829
830
831
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

832
	js_write(cx,argc,arglist);
833
	rc=JS_SUSPENDREQUEST(cx);
834
835
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);
836
	JS_RESUMEREQUEST(cx, rc);
837
838
839
840
841

    return(JS_TRUE);
}

static JSBool
842
js_printf(JSContext *cx, uintN argc, jsval *arglist)
843
{
844
	jsval *argv=JS_ARGV(cx, arglist);
845
846
	char*		p;
	sbbs_t*		sbbs;
847
	jsrefcount	rc;
848

849
850
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

851
852
853
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

854
855
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
856
857
858
		return(JS_FALSE);
	}

859
	rc=JS_SUSPENDREQUEST(cx);
860
	if(sbbs->online==ON_LOCAL)
861
		eprintf(LOG_INFO,"%s",p);
862
863
	else
		sbbs->bputs(p);
864
	JS_RESUMEREQUEST(cx, rc);
865

866
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
867

868
	js_sprintf_free(p);
869
870
871
872
873

    return(JS_TRUE);
}

static JSBool
874
js_alert(JSContext *cx, uintN argc, jsval *arglist)
875
{
876
	jsval *argv=JS_ARGV(cx, arglist);
877
	sbbs_t*		sbbs;
878
	jsrefcount	rc;
deuce's avatar
deuce committed
879
	char		*cstr;
880

881
882
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

deuce's avatar
deuce committed
886
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
887
	if(cstr==NULL)
888
889
	    return(JS_FALSE);

890
	rc=JS_SUSPENDREQUEST(cx);
891
	sbbs->attr(sbbs->cfg.color[clr_err]);
deuce's avatar
deuce committed
892
	sbbs->bputs(cstr);
deuce's avatar
deuce committed
893
	free(cstr);
894
895
	sbbs->attr(LIGHTGRAY);
	sbbs->bputs(crlf);
896
	JS_RESUMEREQUEST(cx, rc);
897

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

900
901
902
903
    return(JS_TRUE);
}

static JSBool
904
js_confirm(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);
deuce's avatar
deuce committed
921
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->yesno(cstr)));
deuce's avatar
deuce committed
922
	free(cstr);
923
	JS_RESUMEREQUEST(cx, rc);
924
925
926
	return(JS_TRUE);
}

927
static JSBool
928
js_deny(JSContext *cx, uintN argc, jsval *arglist)
929
{
930
	jsval *argv=JS_ARGV(cx, arglist);
931
932
	sbbs_t*		sbbs;
	jsrefcount	rc;
deuce's avatar
deuce committed
933
	char		*cstr;
934

935
936
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

937
938
939
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
940
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
941
	if(cstr==NULL)
942
943
944
	    return(JS_FALSE);

	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
945
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->noyes(cstr)));
deuce's avatar
deuce committed
946
	free(cstr);
947
948
949
950
951
	JS_RESUMEREQUEST(cx, rc);
	return(JS_TRUE);
}


952
static JSBool
953
js_prompt(JSContext *cx, uintN argc, jsval *arglist)
954
{
955
	jsval *argv=JS_ARGV(cx, arglist);
956
957
958
	char		instr[81];
    JSString *	str;
	sbbs_t*		sbbs;
959
	jsrefcount	rc;
deuce's avatar
deuce committed
960
    char 		*prompt;
961

962
963
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

	if(argc>1) {
deuce's avatar
deuce committed
972
		JSVALUE_TO_STRBUF(cx, argv[1], instr, sizeof(instr), NULL);
973
974
975
	} else
		instr[0]=0;

976
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
977
	sbbs->bprintf("\1n\1y\1h%s\1w: ",prompt);
deuce's avatar
deuce committed
978
	free(prompt);
979
980

	if(!sbbs->getstr(instr,sizeof(instr)-1,K_EDIT)) {
981
		JS_SET_RVAL(cx, arglist, JSVAL_NULL);
982
		JS_RESUMEREQUEST(cx, rc);
983
984
		return(JS_TRUE);
	}
985
	JS_RESUMEREQUEST(cx, rc);
986
987
988
989

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

990
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
991
992
993
    return(JS_TRUE);
}

994
static jsSyncMethodSpec js_global_functions[] = {
995
	{"log",				js_log,				1,	JSTYPE_STRING,	JSDOCSTR("[level,] value [,value]")
996
	,JSDOCSTR("add a line of text to the server and/or system log, "
997
998
		"<i>values</i> are typically string constants or variables, "
		"<i>level</i> is the debug level/priority (default: <tt>LOG_INFO</tt>)")
999
	,311
1000
	},
1001
1002
	{"read",			js_read,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read up to count characters from input stream")
1003
	,311
1004
1005
1006
	},
	{"readln",			js_readln,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read a single line, up to count characters, from input stream")
1007
	,311
1008
	},
1009
	{"write",			js_write,			0,	JSTYPE_VOID,	JSDOCSTR("value [,value]")
1010
	,JSDOCSTR("send one or more values (typically strings) to the server output")
1011
	,311
1012
	},
1013
1014
	{"write_raw",			js_write_raw,			0,	JSTYPE_VOID,	JSDOCSTR("value [,value]")
	,JSDOCSTR("send a stream of bytes (possibly containing NULLs or special control code sequences) to the server output")
rswindell's avatar
rswindell committed
1015
	,314