main.cpp 158 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 2012 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
255
256
257
258
259

	for(p=addr;*p;p++)
		if(*p!='.' && !isdigit(*p))
			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
	char		*line;
654

655
656
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

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

697
698
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

699
700
701
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

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

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

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

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

	free(buf);
	return(JS_TRUE);
}

static JSBool
722
js_readln(JSContext *cx, uintN argc, jsval *arglist)
723
{
724
	jsval *argv=JS_ARGV(cx, arglist);
725
726
727
	char*		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=(char*)malloc(len))==NULL)
		return(JS_TRUE);

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

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

	free(buf);
	return(JS_TRUE);
}

754
static JSBool
755
js_write(JSContext *cx, uintN argc, jsval *arglist)
756
{
757
	jsval *argv=JS_ARGV(cx, arglist);
758
    uintN		i;
759
    JSString*	str=NULL;
760
	sbbs_t*		sbbs;
761
	jsrefcount	rc;
deuce's avatar
deuce committed
762
	char		*cstr;
763

764
765
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

	if(str==NULL)
784
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
785
	else
786
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
787
788
789
    return(JS_TRUE);
}

790
static JSBool
791
js_write_raw(JSContext *cx, uintN argc, jsval *arglist)
792
{
793
	jsval *argv=JS_ARGV(cx, arglist);
794
795
796
797
    uintN		i;
    char*	str=NULL;
	size_t		len;
	sbbs_t*		sbbs;
798
	jsrefcount	rc;
799

800
801
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

802
803
804
805
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
806
807
		JSVALUE_TO_STRING(cx, argv[i], str, &len);
		if(str==NULL)
808
		    return(JS_FALSE);
809
		rc=JS_SUSPENDREQUEST(cx);
810
		sbbs->putcom(str, len);
811
		JS_RESUMEREQUEST(cx, rc);
812
813
814
815
816
	}

    return(JS_TRUE);
}

817
static JSBool
818
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
819
820
{
	sbbs_t*		sbbs;
821
	jsrefcount	rc;
822

823
824
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

825
826
827
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

828
	js_write(cx,argc,arglist);
829
	rc=JS_SUSPENDREQUEST(cx);
830
831
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);
832
	JS_RESUMEREQUEST(cx, rc);
833
834
835
836
837

    return(JS_TRUE);
}

static JSBool
838
js_printf(JSContext *cx, uintN argc, jsval *arglist)
839
{
840
	jsval *argv=JS_ARGV(cx, arglist);
841
842
	char*		p;
	sbbs_t*		sbbs;
843
	jsrefcount	rc;
844

845
846
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

847
848
849
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

850
851
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
852
853
854
		return(JS_FALSE);
	}

855
	rc=JS_SUSPENDREQUEST(cx);
856
	if(sbbs->online==ON_LOCAL)
857
		eprintf(LOG_INFO,"%s",p);
858
859
	else
		sbbs->bputs(p);
860
	JS_RESUMEREQUEST(cx, rc);
861

862
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
863

864
	js_sprintf_free(p);
865
866
867
868
869

    return(JS_TRUE);
}

static JSBool
870
js_alert(JSContext *cx, uintN argc, jsval *arglist)
871
{
872
	jsval *argv=JS_ARGV(cx, arglist);
873
	sbbs_t*		sbbs;
874
	jsrefcount	rc;
deuce's avatar
deuce committed
875
	char		*cstr;
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
	JSVALUE_TO_STRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
883
	if(cstr==NULL)
884
885
	    return(JS_FALSE);

886
	rc=JS_SUSPENDREQUEST(cx);
887
	sbbs->attr(sbbs->cfg.color[clr_err]);
deuce's avatar
deuce committed
888
	sbbs->bputs(cstr);
889
890
	sbbs->attr(LIGHTGRAY);
	sbbs->bputs(crlf);
891
	JS_RESUMEREQUEST(cx, rc);
892

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

895
896
897
898
    return(JS_TRUE);
}

static JSBool
899
js_confirm(JSContext *cx, uintN argc, jsval *arglist)
900
{
901
	jsval *argv=JS_ARGV(cx, arglist);
902
	sbbs_t*		sbbs;
903
	jsrefcount	rc;
deuce's avatar
deuce committed
904
	char		*cstr;
905

906
907
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

908
909
910
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

911
	JSVALUE_TO_STRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
912
	if(cstr==NULL)
913
914
	    return(JS_FALSE);

915
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
916
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->yesno(cstr)));
917
	JS_RESUMEREQUEST(cx, rc);
918
919
920
	return(JS_TRUE);
}

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

929
930
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

931
932
933
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

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

	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
939
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->noyes(cstr)));
940
941
942
943
944
	JS_RESUMEREQUEST(cx, rc);
	return(JS_TRUE);
}


945
static JSBool
946
js_prompt(JSContext *cx, uintN argc, jsval *arglist)
947
{
948
	jsval *argv=JS_ARGV(cx, arglist);
949
950
951
	char		instr[81];
    JSString *	str;
	sbbs_t*		sbbs;
952
	jsrefcount	rc;
deuce's avatar
deuce committed
953
954
	char		*cstr;
    char 		*prompt;
955

956
957
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

958
959
960
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

961
	JSVALUE_TO_STRING(cx, argv[0], prompt, NULL);
deuce's avatar
deuce committed
962
	if(prompt==NULL)
963
964
965
	    return(JS_FALSE);

	if(argc>1) {
966
		JSVALUE_TO_STRING(cx, argv[1], cstr, NULL);
deuce's avatar
deuce committed
967
		if(cstr==NULL)
968
		    return(JS_FALSE);
deuce's avatar
deuce committed
969
		SAFECOPY(instr,cstr);
970
971
972
	} else
		instr[0]=0;

973
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
974
	sbbs->bprintf("\1n\1y\1h%s\1w: ",prompt);
975
976

	if(!sbbs->getstr(instr,sizeof(instr)-1,K_EDIT)) {
977
		JS_SET_RVAL(cx, arglist, JSVAL_NULL);
978
		JS_RESUMEREQUEST(cx, rc);
979
980
		return(JS_TRUE);
	}
981
	JS_RESUMEREQUEST(cx, rc);
982
983
984
985

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

986
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
987
988
989
    return(JS_TRUE);
}

990
static jsSyncMethodSpec js_global_functions[] = {
991
	{"log",				js_log,				1,	JSTYPE_STRING,	JSDOCSTR("[level,] value [,value]")
992
	,JSDOCSTR("add a line of text to the server and/or system log, "
993
994
		"<i>values</i> are typically string constants or variables, "
		"<i>level</i> is the debug level/priority (default: <tt>LOG_INFO</tt>)")
995
	,311
996
	},
997
998
	{"read",			js_read,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read up to count characters from input stream")
999
	,311
1000
1001
1002
	},
	{"readln",			js_readln,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read a single line, up to count characters, from input stream")
1003
	,311
1004
	},
1005
	{"write",			js_write,			0,	JSTYPE_VOID,	JSDOCSTR("value [,value]")
1006
	,JSDOCSTR("send one or more values (typically strings) to the server output")
1007
	,311
1008
	},
1009
1010
	{"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
1011
	,314
1012
	},
1013
1014
1015
1016
	{"print",			js_writeln,			0,	JSTYPE_ALIAS },
    {"writeln",         js_writeln,         0,	JSTYPE_VOID,	JSDOCSTR("value [,value]")
	,JSDOCSTR("send a line of text to the console or event log with automatic line termination (CRLF), "
		"<i>values</i> are typically string constants or variables (AKA print)")
1017
	,311
1018
	},
1019
    {"printf",          js_printf,          1,	JSTYPE_STRING,	JSDOCSTR("string format [,value][,value]")
1020
	,JSDOCSTR("print a formatted string - <small>CAUTION: for experienced C programmers ONLY</small>")
1021
	,310
1022