main.cpp 156 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 2011 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
45
46

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

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

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
rswindell's avatar
rswindell committed
74
	#define SSH_END()	if(ssh)	cryptDestroySession(sbbs->ssh_session);
75
76
77
78
#else
	#define	SSH_END()
#endif

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

82
83
static	volatile ulong node_threads_running=0;
static	volatile ulong thread_count=0;
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;

113
static void status(const char* str)
114
115
{
	if(startup!=NULL && startup->status!=NULL)
116
	    startup->status(startup->cbdata,str);
117
118
119
120
121
}

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

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

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

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

static void thread_down()
{
	if(thread_count>0)
		thread_count--;
	if(startup!=NULL && startup->thread_up!=NULL)
149
		startup->thread_up(startup->cbdata,FALSE,FALSE);
150
151
}

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

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

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

168
    return(startup->lputs(startup->cbdata,level,str));
169
170
}

171
int lprintf(int level, const char *fmt, ...)
172
173
174
175
176
177
178
179
{
	va_list argptr;
	char sbuf[1024];

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

183
int eprintf(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
193
194
195
196
197
198
199
200
201

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

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

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

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

	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) 
226
		startup->socket_open(startup->cbdata,TRUE);
227
228
229
230
231
232
233
234
235
236
237
238
239

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


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

	if(*addr==0)
254
		return((u_long)INADDR_NONE);
255
256
257
258
259
260
261

	for(p=addr;*p;p++)
		if(*p!='.' && !isdigit(*p))
			break;
	if(!(*p))
		return(inet_addr(addr));
	if((host=gethostbyname(addr))==NULL) 
262
		return((u_long)INADDR_NONE);
263
264
265
266
267
	return(*((ulong*)host->h_addr_list[0]));
}

} /* extern "C" */

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

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

#else /* No WINSOCK */

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

#endif

295
296
DLLEXPORT void DLLCALL sbbs_srand()
{
297
	DWORD seed;
298

299
	xp_randomize();
300
301
302
303
304
305
306
#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);
	}
307
#else
308
	seed = time(NULL) ^ (DWORD)GetCurrentThreadId();
309
310
311
#endif

 	srand(seed);
312
313
314
	sbbs_random(10);	/* Throw away first number */
}

315
int DLLCALL sbbs_random(int n)
316
317
318
319
{
	return(xp_random(n));
}

320
321
#ifdef JAVASCRIPT

322
323
static js_server_props_t js_server_props;

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

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

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

354
	return(JS_TRUE);
355
356
}

357
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
358
359

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

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

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

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

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

388
#ifdef BUILD_JSDOCS
389
390

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

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

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

	return(JS_TRUE);
}

443
JSBool 
444
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
445
446
447
{
	int			i;
	jsuint		len=0;
448
	long		ver;
449
450
451
452
453
454
455
456
457
458
459
460
	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);

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

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

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

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

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

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

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

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

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

	return(JS_TRUE);
}

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

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

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

552
553
554
	return(ret);
}

555
#else // NON-JSDOCS
556

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

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

	return(JS_TRUE);
}


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

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

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

	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);
			}
		}
	}
609
	if(consts) {
deuce's avatar
deuce committed
610
		for(i=0;consts[i].name;i++) {
611
			if(name==NULL || strcmp(name, consts[i].name)==0) {
deuce's avatar
deuce committed
612
	        	if(!JS_NewNumberValue(cx, consts[i].val, &val))
613
614
					return(JS_FALSE);

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

				if(name)
					return(JS_TRUE);
			}
		}
	}
623
624
625
626

	return(JS_TRUE);
}

627
628
#endif

629
630
631
632
633
634
635
636
637
638
639
640
641
642
/* 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++) {
        if(!JS_NewNumberValue(cx, ints[i].val, &val))
			return(JS_FALSE);

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

644
645
646
	return(JS_TRUE);
}

647
648
649
650
651
652
653
654
655
656
657
658
659
660
char*
DLLCALL js_ValueToStringBytes(JSContext* cx, jsval val, size_t* len)
{
	JSString* str;
	
	if((str=JS_ValueToString(cx, val))==NULL)
		return(NULL);

	if(len!=NULL)
		*len = JS_GetStringLength(str);

	return(JS_GetStringBytes(str));
}

661
662
663
static JSBool
js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
664
665
    uintN		i=0;
	int32		level=LOG_INFO;
666
    JSString*	str=NULL;
667
	sbbs_t*		sbbs;
668
	jsrefcount	rc;
669
670
671
672

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

673
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i]))
674
675
676
		JS_ValueToInt32(cx,argv[i++],&level);

    for(; i<argc; i++) {
677
		if((str=JS_ValueToString(cx, argv[i]))==NULL) {
678
			JS_RESUMEREQUEST(cx, rc);
679
		    return(JS_FALSE);
680
		}
681
		rc=JS_SUSPENDREQUEST(cx);
682
		if(sbbs->online==ON_LOCAL) {
683
			if(startup!=NULL && startup->event_lputs!=NULL && level <= startup->log_level)
684
				startup->event_lputs(startup->event_cbdata,level,JS_GetStringBytes(str));
685
		} else
686
			lprintf(level,"Node %d %s", sbbs->cfg.node_num, JS_GetStringBytes(str));
687
		JS_RESUMEREQUEST(cx, rc);
688
	}
689

690
691
692
693
	if(str==NULL)
		*rval = JSVAL_VOID;
	else
		*rval = STRING_TO_JSVAL(str);
694
695
696
    return(JS_TRUE);
}

697
698
699
700
701
702
static JSBool
js_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	uchar*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
703
	jsrefcount	rc;
704
705
706
707
708
709
710
711
712
713

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

	if(argc)
		JS_ValueToInt32(cx,argv[0],&len);

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

714
	rc=JS_SUSPENDREQUEST(cx);
715
	len=RingBufRead(&sbbs->inbuf,buf,len);
716
	JS_RESUMEREQUEST(cx, rc);
717
718
719
720
721
722
723
724
725
726
727
728
729
730

	if(len>0)
		*rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len));

	free(buf);
	return(JS_TRUE);
}

static JSBool
js_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
731
	jsrefcount	rc;
732
733
734
735
736
737
738
739
740
741

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

	if(argc)
		JS_ValueToInt32(cx,argv[0],&len);

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

742
	rc=JS_SUSPENDREQUEST(cx);
743
	len=sbbs->getstr(buf,len,K_NONE);
744
	JS_RESUMEREQUEST(cx, rc);
745
746
747
748
749
750
751
752

	if(len>0)
		*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf));

	free(buf);
	return(JS_TRUE);
}

753
static JSBool
754
js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
755
756
{
    uintN		i;
757
    JSString*	str=NULL;
758
	sbbs_t*		sbbs;
759
	jsrefcount	rc;
760
761
762
763
764
765
766

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

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

	if(str==NULL)
		*rval = JSVAL_VOID;
	else
		*rval = STRING_TO_JSVAL(str);
    return(JS_TRUE);
}

782
783
784
785
786
787
788
static JSBool
js_write_raw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    uintN		i;
    char*	str=NULL;
	size_t		len;
	sbbs_t*		sbbs;
789
	jsrefcount	rc;
790
791
792
793
794
795
796

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

    for (i = 0; i < argc; i++) {
		if((str=js_ValueToStringBytes(cx, argv[i], &len))==NULL)
		    return(JS_FALSE);
797
		rc=JS_SUSPENDREQUEST(cx);
798
		sbbs->putcom(str, len);
799
		JS_RESUMEREQUEST(cx, rc);
800
801
802
803
804
	}

    return(JS_TRUE);
}

805
static JSBool
806
js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
807
808
{
	sbbs_t*		sbbs;
809
	jsrefcount	rc;
810
811
812
813
814

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

	js_write(cx,obj,argc,argv,rval);
815
	rc=JS_SUSPENDREQUEST(cx);
816
817
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);
818
	JS_RESUMEREQUEST(cx, rc);
819
820
821
822
823
824
825
826
827

    return(JS_TRUE);
}

static JSBool
js_printf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*		p;
	sbbs_t*		sbbs;
828
	jsrefcount	rc;
829
830
831
832

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

833
834
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
835
836
837
		return(JS_FALSE);
	}

838
	rc=JS_SUSPENDREQUEST(cx);
839
	if(sbbs->online==ON_LOCAL)
840
		eprintf(LOG_INFO,"%s",p);
841
842
	else
		sbbs->bputs(p);
843
	JS_RESUMEREQUEST(cx, rc);
844
845
846

	*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p));

847
	js_sprintf_free(p);
848
849
850
851
852
853
854
855
856

    return(JS_TRUE);
}

static JSBool
js_alert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *	str;
	sbbs_t*		sbbs;
857
	jsrefcount	rc;
858
859
860
861
862
863
864

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

	if((str=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

865
	rc=JS_SUSPENDREQUEST(cx);
866
867
868
869
	sbbs->attr(sbbs->cfg.color[clr_err]);
	sbbs->bputs(JS_GetStringBytes(str));
	sbbs->attr(LIGHTGRAY);
	sbbs->bputs(crlf);
870
	JS_RESUMEREQUEST(cx, rc);
871
872
873
874
875
876
877
878
879

    return(JS_TRUE);
}

static JSBool
js_confirm(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *	str;
	sbbs_t*		sbbs;
880
	jsrefcount	rc;
881
882
883
884
885
886
887

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

	if((str=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

888
	rc=JS_SUSPENDREQUEST(cx);
889
	*rval = BOOLEAN_TO_JSVAL(sbbs->yesno(JS_GetStringBytes(str)));
890
	JS_RESUMEREQUEST(cx, rc);
891
892
893
	return(JS_TRUE);
}

894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
static JSBool
js_deny(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *	str;
	sbbs_t*		sbbs;
	jsrefcount	rc;

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

	if((str=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

	rc=JS_SUSPENDREQUEST(cx);
	*rval = BOOLEAN_TO_JSVAL(sbbs->noyes(JS_GetStringBytes(str)));
	JS_RESUMEREQUEST(cx, rc);
	return(JS_TRUE);
}


914
915
916
917
918
919
920
static JSBool
js_prompt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char		instr[81];
    JSString *	prompt;
    JSString *	str;
	sbbs_t*		sbbs;
921
	jsrefcount	rc;
922
923
924
925
926
927
928
929
930
931
932
933
934
935

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

	if((prompt=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

	if(argc>1) {
		if((str=JS_ValueToString(cx, argv[1]))==NULL)
		    return(JS_FALSE);
		SAFECOPY(instr,JS_GetStringBytes(str));
	} else
		instr[0]=0;

936
	rc=JS_SUSPENDREQUEST(cx);
937
938
939
940
	sbbs->bprintf("\1n\1y\1h%s\1w: ",JS_GetStringBytes(prompt));

	if(!sbbs->getstr(instr,sizeof(instr)-1,K_EDIT)) {
		*rval = JSVAL_NULL;
941
		JS_RESUMEREQUEST(cx, rc);
942
943
		return(JS_TRUE);
	}
944
	JS_RESUMEREQUEST(cx, rc);
945
946
947
948
949
950
951
952

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

	*rval = STRING_TO_JSVAL(str);
    return(JS_TRUE);
}

953
static jsSyncMethodSpec js_global_functions[] = {
954
	{"log",				js_log,				1,	JSTYPE_STRING,	JSDOCSTR("[level,] value [,value]")
955
	,JSDOCSTR("add a line of text to the server and/or system log, "
956
957
		"<i>values</i> are typically string constants or variables, "
		"<i>level</i> is the debug level/priority (default: <tt>LOG_INFO</tt>)")
958
	,311
959
	},
960
961
	{"read",			js_read,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read up to count characters from input stream")
962
	,311
963
964
965
	},
	{"readln",			js_readln,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read a single line, up to count characters, from input stream")
966
	,311
967
	},
968
	{"write",			js_write,			0,	JSTYPE_VOID,	JSDOCSTR("value [,value]")
969
	,JSDOCSTR("send one or more values (typically strings) to the server output")
970
	,311
971
	},
972
973
	{"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
974
	,314
975
	},
976
977
978
979
	{"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)")
980
	,311
981
	},
982
    {"printf",          js_printf,          1,	JSTYPE_STRING,	JSDOCSTR("string format [,value][,value]")
983
	,JSDOCSTR("print a formatted string - <small>CAUTION: for experienced C programmers ONLY</small>")
984
	,310
985
986
987
	},	
	{"alert",			js_alert,			1,	JSTYPE_VOID,	JSDOCSTR("value")
	,JSDOCSTR("print an alert message (ala client-side JS)")
988
	,310
989
990
991
	},
	{"prompt",			js_prompt,			1,	JSTYPE_STRING,	JSDOCSTR("[value]")
	,JSDOCSTR("displays a prompt (<i>value</i>) and returns a string of user input (ala clent-side JS)")
992
	,310
993
994
995
	},
	{"confirm",			js_confirm,			1,	JSTYPE_BOOLEAN,	JSDOCSTR("value")
	,JSDOCSTR("displays a Yes/No prompt and returns <i>true</i> or <i>false</i> "
996
		"based on user's confirmation (ala client-side JS, <i>true</i> = yes)")
997
	,310
998
	},
999
1000
1001
1002
1003
	{"deny",			js_deny,			1,	JSTYPE_BOOLEAN,	JSDOCSTR("value")
	,JSDOCSTR("displays a No/Yes prompt and returns <i>true</i> or <i>false</i> "
		"based on user's denial (<i>true</i> = no)")
	,31501
	},
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
    {0}
};

static void
js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
	char	line[64];
	char	file[MAX_PATH+1];
	sbbs_t*	sbbs;
	const char*	warning;
1014
	jsrefcount	rc;
1015
	int		log_level;
1016
	char	nodestr[128];
1017
1018
1019

	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return;
1020
1021
1022
1023
1024

    if(sbbs->cfg.node_num)
    	SAFEPRINTF(nodestr,"Node %d",sbbs->cfg.node_num);
    else
    	SAFECOPY(nodestr,sbbs->client_name);
1025
1026
	
	if(report==NULL) {
1027
		lprintf(LOG_ERR,"%s !JavaScript: %s", nodestr, message);
1028
1029
1030
1031
		return;
    }

	if(report->filename)
1032
		SAFEPRINTF(file," %s",report->filename);
1033
1034
1035
1036
	else
		file[0]=0;

	if(report->lineno)
1037
		SAFEPRINTF(line," line %d",report->lineno);
1038
1039
1040
1041
1042
1043
1044
1045
	else
		line[0]=0;

	if(JSREPORT_IS_WARNING(report->flags)) {
		if(JSREPORT_IS_STRICT(report->flags))
			warning="strict warning";
		else
			warning="warning";
1046
1047
		log_level = LOG_WARNING;
	} else {
1048
		warning=nulstr;
1049
1050
		log_level = LOG_ERR;
	}
1051

1052
	rc=JS_SUSPENDREQUEST(cx);
1053
	if(sbbs->online==ON_LOCAL) 
1054
		eprintf(log_level,"!JavaScript %s%s%s: %s",warning,file,line,message);
1055
	else {
1056
		lprintf(log_level,"%s !JavaScript %s%s%s: %s",nodestr,warning,file,line,message);
1057
1058
		sbbs->bprintf("!JavaScript %s%s%s: %s\r\n",warning,file,line,message);
	}
1059
	JS_RESUMEREQUEST(cx, rc);
1060
1061
}

1062
bool sbbs_t::js_init(ulong* stack_frame)
1063
1064
1065
1066
{
	char		node[128];

    if(cfg.node_num)
1067
    	SAFEPRINTF(node,"Node %d",cfg.node_num);
1068
    else
1069
    	SAFECOPY(node,client_name);
1070

1071
1072
1073
	if(startup->js.max_bytes==0)			startup->js.max_bytes=JAVASCRIPT_MAX_BYTES;
	if(startup->js.cx_stack==0)				startup->js.cx_stack=JAVASCRIPT_CONTEXT_STACK;

1074
	lprintf(LOG_DEBUG,"%s JavaScript: Creating runtime: %lu bytes"
1075
		,node,startup->js.max_bytes);
1076

1077
	if((js_runtime = jsrt_GetNew(startup->js.max_bytes, 1000, __FILE__, __LINE__))==NULL)
1078
1079
		return(false);

1080
	lprintf(LOG_DEBUG,"%s JavaScript: Initializing context (stack: %lu bytes)"
1081
		,node,startup->js.cx_stack);
1082

1083
    if((js_cx = JS_NewContext(js_runtime, startup->js.cx_stack))==NULL)
1084
		return(false);
1085
	JS_BEGINREQUEST(js_cx);
rswindell's avatar
rswindell committed
1086
1087
	
	memset(&js_branch,0,sizeof(js_branch));
1088
1089
1090
	js_branch.limit = startup->js.branch_limit;
	js_branch.gc_interval = startup->js.gc_interval;
	js_branch.yield_interval = startup->js.yield_interval;
rswindell's avatar
rswindell committed
1091
	js_branch.terminated = &terminated;
1092
	js_branch.auto_terminate = TRUE;
1093
1094
1095
1096
1097
1098
1099
1100
1101

	bool success=false;

	do {

		JS_SetErrorReporter(js_cx, js_ErrorReporter);

		JS_SetContextPrivate(js_cx, this);	/* Store a pointer to sbbs_t instance */

1102
		/* Global Objects (including system, js, client, Socket, MsgBase, File, User, etc. */
1103
		if((js_glob=js_CreateCommonObjects(js_cx, &scfg, &cfg, js_global_functions
1104
1105
					,uptime, startup->host_name, SOCKLIB_DESC	/* system */
					,&js_branch									/* js */
1106
					,&startup->js
1107
					,&client, client_socket						/* client */
1108
					,&js_server_props							/* server */
1109
			))==NULL)
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
			break;

		/* BBS Object */
		if(js_CreateBbsObject(js_cx, js_glob)==NULL)
			break;

		/* Console Object */
		if(js_CreateConsoleObject(js_cx, js_glob)==NULL)
			break;

1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
		if(startup->js.thread_stack) {
			ulong stack_limit;

#if JS_STACK_GROWTH_DIRECTION > 0
			stack_limit=((ulong)stack_frame)+startup->js.thread_stack;
#else
			stack_limit=((ulong)stack_frame)-startup->js.thread_stack;
#endif
			JS_SetThreadStackLimit(js_cx, stack_limit);

			lprintf(LOG_DEBUG,"%s JavaScript: Thread stack limit: %lu bytes"
				,node, startup->js.thread_stack);
		}

1134
1135
1136
1137
		success=true;

	} while(0);

1138
	JS_ENDREQUEST(js_cx);
1139
1140
1141
1142
1143
1144
1145
1146
1147
	if(!success) {
		JS_DestroyContext(js_cx);
		js_cx=NULL;
		return(false);
	}

	return(true);
}

1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
void sbbs_t::js_cleanup(const char* node)
{
	/* Free Context */
	if(js_cx!=NULL) {	
		lprintf(LOG_DEBUG,"%s JavaScript: Destroying context",node);
		JS_DestroyContext(js_cx);
		js_cx=NULL;
	}

	if(js_runtime!=NULL) {
		lprintf(LOG_DEBUG,"%s JavaScript: Destroying runtime",node);
		jsrt_Release(js_runtime);
		js_runtime=NULL;
	}
}

1164
1165
1166
1167
void sbbs_t::js_create_user_objects(void)
{
	if(js_cx==NULL)
		return;
1168
	
1169
	JS_BEGINREQUEST(js_cx);
rswindell's avatar
rswindell committed
1170
	if(!js_CreateUserObjects(js_cx, js_glob, &cfg, &useron, &client, NULL, subscan)) 
1171
		lprintf(LOG_ERR,"!JavaScript ERROR creating user objects");
1172
	JS_ENDREQUEST(js_cx);
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
}

#endif	/* JAVASCRIPT */

static BYTE* telnet_interpret(sbbs_t* sbbs, BYTE* inbuf, int inlen,
  									BYTE* outbuf, int& outlen)
{
	BYTE*   first_iac=NULL;
	BYTE*	first_cr=NULL;
	int 	i;

1184
1185
1186
1187
1188