main.cpp 164 KB
Newer Older
1
/* Synchronet terminal server thread and related functions */
2
3

/* $Id$ */
4
// vi: tabstop=4
5
6
7
8
9

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
#include "ident.h"
#include "telnet.h" 
40
#include "netwrap.h"
41
#include "js_rtpool.h"
42
#include "js_request.h"
43
#include "ssl.h"
deuce's avatar
deuce committed
44
45
#include <multisock.h>
#include <limits.h>		// HOST_NAME_MAX
46
47
48
49
50

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

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

54
#define TELNET_SERVER "Synchronet Terminal Server"
55
56
57
58
#define STATUS_WFC	"Listening"

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

// Globals
#ifdef _WIN32
	HANDLE		exec_mutex=NULL;
	HINSTANCE	hK32=NULL;

	#if defined(_DEBUG) && defined(_MSC_VER)
			HANDLE	debug_log=INVALID_HANDLE_VALUE;
		   _CrtMemState mem_chkpoint;
	#endif // _DEBUG && _MSC_VER

#endif // _WIN32

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

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

82
static	protected_uint32_t node_threads_running;
83
84
85
86
87
88
89
90
		
char 	lastuseron[LEN_ALIAS+1];  /* Name of user last online */
RingBuf* node_inbuf[MAX_NODES];
SOCKET	spy_socket[MAX_NODES];
#ifdef __unix__
SOCKET	uspy_socket[MAX_NODES];	  /* UNIX domain spy sockets */
#endif
SOCKET	node_socket[MAX_NODES];
deuce's avatar
deuce committed
91
struct xpms_set				*ts_set;
92
93
94
95
96
static	sbbs_t*	sbbs=NULL;
static	scfg_t	scfg;
static	char *	text[TOTAL_TEXT];
static	WORD	first_node;
static	WORD	last_node;
97
static	bool	terminate_server=false;
98
99
static	str_list_t recycle_semfiles;
static	str_list_t shutdown_semfiles;
100
101
static	link_list_t current_logins;
static	link_list_t current_connections;
102
103
104
#ifdef _THREAD_SUID_BROKEN
int	thread_suid_broken=TRUE;			/* NPTL is no longer broken */
#endif
105
106
107
108
109

extern "C" {

static bbs_startup_t* startup=NULL;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

205
	strip_ctrl(sbuf, sbuf);
206

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

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

218
219
220
221
222
223
224
225
226
227
228
229
int sbbs_t::lprintf(int level, const char *fmt, ...)
{
	va_list argptr;
	char sbuf[1024];

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

deuce's avatar
deuce committed
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
struct main_sock_cb_data {
	bbs_startup_t	*startup;
	const char		*protocol;
};

void sock_cb(SOCKET sock, void *cb_data)
{
	char	error_str[256];
	struct main_sock_cb_data *cb=(struct main_sock_cb_data *)cb_data;

	if(cb->startup && cb->startup->socket_open)
		cb->startup->socket_open(cb->startup->cbdata, TRUE);
	if(set_socket_options(&scfg, sock, cb->protocol, error_str, sizeof(error_str)))
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error_str);
}

void sock_close_cb(SOCKET sock, void *cb_data)
{
	bbs_startup_t	*su=(bbs_startup_t *)cb_data;

	if(su && su->socket_open)
		su->socket_open(su->cbdata, FALSE);
}

254
SOCKET open_socket(int type, const char* protocol)
255
256
257
258
259
260
{
	SOCKET	sock;
	char	error[256];

	sock=socket(AF_INET, type, IPPROTO_IP);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
261
		startup->socket_open(startup->cbdata,TRUE);
262
	if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
263
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
264
265
266
267

	return(sock);
}

deuce's avatar
deuce committed
268
SOCKET accept_socket(SOCKET s, union xp_sockaddr* addr, socklen_t* addrlen)
269
270
271
{
	SOCKET	sock;

deuce's avatar
deuce committed
272
273
	sock=accept(s,&addr->addr,addrlen);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
274
		startup->socket_open(startup->cbdata,TRUE);
275
276
277
278
279
280
281
282
283
284
285
286
287

	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);
288
	if(startup!=NULL && startup->socket_open!=NULL)
289
		startup->socket_open(startup->cbdata,FALSE);
290
	if(result!=0 && ERROR_VALUE!=ENOTSOCK)
291
		lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
292
293
294
	return(result);
}

deuce's avatar
deuce committed
295
/* TODO: IPv6 */
296
297
298
299
300
301
u_long resolve_ip(char *addr)
{
	HOSTENT*	host;
	char*		p;

	if(*addr==0)
302
		return((u_long)INADDR_NONE);
303
304

	for(p=addr;*p;p++)
305
		if(*p!='.' && !isdigit((uchar)*p))
306
307
308
309
			break;
	if(!(*p))
		return(inet_addr(addr));
	if((host=gethostbyname(addr))==NULL) 
310
		return((u_long)INADDR_NONE);
311
312
313
314
315
	return(*((ulong*)host->h_addr_list[0]));
}

} /* extern "C" */

316
317
318
319
320
321
322
323
324
325
326
#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) {
327
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
328
329
330
331
		WSAInitialized=TRUE;
		return(TRUE);
	}

332
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
333
334
335
336
337
338
339
340
341
342
	return(FALSE);
}

#else /* No WINSOCK */

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

#endif

343
344
DLLEXPORT void DLLCALL sbbs_srand()
{
345
	DWORD seed;
346

347
	xp_randomize();
348
#if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
349
	int     rf,rd=0;
350

351
352
	if((rf=open(RANDOM_DEV, O_RDONLY|O_NONBLOCK))!=-1) {
		rd=read(rf, &seed, sizeof(seed));
353
354
		close(rf);
	}
deuce's avatar
deuce committed
355
	if (rd != sizeof(seed))
356
#endif
357
		seed = time32(NULL) ^ (uintmax_t)GetCurrentThreadId();
358
359

 	srand(seed);
360
361
362
	sbbs_random(10);	/* Throw away first number */
}

363
int DLLCALL sbbs_random(int n)
364
365
366
367
{
	return(xp_random(n));
}

368
369
#ifdef JAVASCRIPT

370
371
static js_server_props_t js_server_props;

372
373
374
375
376
377
378
379
380
381
382
383
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
384
385
		if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)	/* Assertion here, in _heap_alloc_dbg, June-21-2004 */
			return(JS_FALSE);								/* Caused by nntpservice.js? */
386

387
388
389
390
	if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
		,NULL,NULL,flags))
		return(JS_FALSE);

391
392
393
394
395
396
397
398
399
400
401
	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;
	}

402
	return(JS_TRUE);
403
404
}

405
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
406
407

JSBool
408
DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
409
410
411
412
413
414
{
	JSString* js_str = JS_NewStringCopyZ(cx, str);

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

rswindell's avatar
rswindell committed
415
416
417
	if(ver < 10000)		/* auto convert 313 to 31300 */
		ver*=100;

418
	return(JS_DefineProperty(cx,obj,"_description"
419
420
421
			,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
		&& JS_DefineProperty(cx,obj,"_ver"
			,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
422
423
424
}

JSBool
425
DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
426
427
428
429
430
431
432
433
434
435
{
	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));
}

436
#ifdef BUILD_JSDOCS
437
438

static const char* method_array_name = "_method_list";
439
static const char* propver_array_name = "_property_ver_list";
440
441
442
443
444
445
446
447
448
449
450
451
452

/*
 * 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
453
454
	"null",
	"xml",
455
456
	"array",
	"alias",
deuce's avatar
deuce committed
457
	"undefined"
458
459
};

460
461
462
463
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint		i;
464
	long		ver;
465
466
467
468
469
470
471
472
473
474
475
476
	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++) {
477
478
		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))
479
480
			return(JS_FALSE);
		if(props[i].flags&JSPROP_ENUMERATE) {	/* No need to version invisible props */
481
482
483
			if((ver=props[i].ver) < 10000)		/* auto convert 313 to 31300 */
				ver*=100;
			val = INT_TO_JSVAL(ver);
484
485
486
487
488
489
490
491
			if(!JS_SetElement(cx, array, len++, &val))
				return(JS_FALSE);
		}
	}

	return(JS_TRUE);
}

492
JSBool 
deuce's avatar
deuce committed
493
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
494
495
496
{
	int			i;
	jsuint		len=0;
497
	long		ver;
498
499
500
501
	jsval		val;
	JSObject*	method;
	JSObject*	method_array;
	JSString*	js_str;
deuce's avatar
deuce committed
502
503
	size_t		str_len=0;
	char		*str=NULL;
504
505

	/* Return existing method_list array if it's already been created */
deuce's avatar
deuce committed
506
	if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID) {
507
		method_array=JSVAL_TO_OBJECT(val);
deuce's avatar
deuce committed
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
		// If the first item is already in the list, don't do anything.
		if(!JS_GetArrayLength(cx, method_array, &len))
			return(JS_FALSE);
		for(i=0; i<len; i++) {
			if(JS_GetElement(cx, method_array, i, &val)!=JS_TRUE || val == JSVAL_VOID)
				continue;
			JS_GetProperty(cx, JSVAL_TO_OBJECT(val), "name", &val);
			JSVALUE_TO_RASTRING(cx, val, str, &str_len, NULL);
			if(str==NULL)
				continue;
			if(strcmp(str, funcs[0].name)==0)
				return(JS_TRUE);
		}
	}
	else {
523
524
		if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL) 
			return(JS_FALSE);
deuce's avatar
deuce committed
525
526
		if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
				, NULL, NULL, 0))
527
			return(JS_FALSE);
deuce's avatar
deuce committed
528
	}
529
530
531

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

532
533
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
534
535
536
537

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

538
		method = JS_NewObject(cx, NULL, NULL, method_array);	/* exception here June-7-2003 */
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572

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

573
		if(funcs[i].ver) {
574
			if((ver=funcs[i].ver) < 10000)		/* auto convert 313 to 31300 */
575
576
				ver*=100;
			val = INT_TO_JSVAL(ver);
577
578
579
			JS_SetProperty(cx,method, "ver", &val);
		}

580
581
582
583
584
585
586
587
		val=OBJECT_TO_JSVAL(method);
		if(!JS_SetElement(cx, method_array, len+i, &val))
			return(JS_FALSE);
	}

	return(JS_TRUE);
}

588
589
590
591
592
/*
 * Always resolve all here since
 * 1) We'll always be enumerating anyways
 * 2) The speed penalty won't be seen in production code anyways
 */
593
594
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
595
596
597
{
	JSBool	ret=JS_TRUE;

deuce's avatar
deuce committed
598
	if(props) {
599
600
		if(!js_DefineSyncProperties(cx, obj, props))
			ret=JS_FALSE;
deuce's avatar
deuce committed
601
	}
602
		
deuce's avatar
deuce committed
603
	if(funcs) {
deuce's avatar
deuce committed
604
		if(!js_DefineSyncMethods(cx, obj, funcs))
605
			ret=JS_FALSE;
deuce's avatar
deuce committed
606
	}
607

deuce's avatar
deuce committed
608
609
610
611
	if(consts) {
		if(!js_DefineConstIntegers(cx, obj, consts, flags))
			ret=JS_FALSE;
	}
612

613
614
615
	return(ret);
}

616
#else // NON-JSDOCS
617

618
619
620
621
622
623
624
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint i;

	for(i=0;props[i].name;i++) 
		if(!JS_DefinePropertyWithTinyId(cx, obj, 
625
			props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
626
627
628
629
630
631
			return(JS_FALSE);

	return(JS_TRUE);
}


632
JSBool 
deuce's avatar
deuce committed
633
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
634
{
635
	uint i;
636
637

	for(i=0;funcs[i].name;i++)
638
639
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
640
641
642
	return(JS_TRUE);
}

643
644
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
645
646
{
	uint i;
647
	jsval	val;
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669

	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);
			}
		}
	}
670
	if(consts) {
deuce's avatar
deuce committed
671
		for(i=0;consts[i].name;i++) {
672
			if(name==NULL || strcmp(name, consts[i].name)==0) {
673
				val=INT_TO_JSVAL(consts[i].val);
674

deuce's avatar
deuce committed
675
				if(!JS_DefineProperty(cx, obj, consts[i].name, val ,NULL, NULL, flags))
676
677
678
679
680
681
682
					return(JS_FALSE);

				if(name)
					return(JS_TRUE);
			}
		}
	}
683
684
685
686

	return(JS_TRUE);
}

687
688
#endif

689
690
691
692
693
694
695
696
/* 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++) {
697
		val=INT_TO_JSVAL(ints[i].val);
698
699
700
701

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

703
704
705
	return(JS_TRUE);
}

706
static JSBool
707
js_log(JSContext *cx, uintN argc, jsval *arglist)
708
{
709
	jsval *argv=JS_ARGV(cx, arglist);
710
711
    uintN		i=0;
	int32		level=LOG_INFO;
712
    JSString*	str=NULL;
713
	sbbs_t*		sbbs;
714
	jsrefcount	rc;
deuce's avatar
deuce committed
715
716
	char		*line=NULL;
	size_t		line_sz=0;
717

718
719
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

723
724
725
726
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
727
728

    for(; i<argc; i++) {
rswindell's avatar
rswindell committed
729
730
		if((str=JS_ValueToString(cx, argv[i]))==NULL) {
			FREE_AND_NULL(line);
deuce's avatar
deuce committed
731
			return(JS_FALSE);
rswindell's avatar
rswindell committed
732
		}
deuce's avatar
deuce committed
733
		JSSTRING_TO_RASTRING(cx, str, line, &line_sz, NULL);
deuce's avatar
deuce committed
734
		if(line==NULL)
735
		    return(JS_FALSE);
736
		rc=JS_SUSPENDREQUEST(cx);
737
		if(sbbs->online==ON_LOCAL) {
deuce's avatar
deuce committed
738
739
740
			if(startup!=NULL && startup->event_lputs!=NULL && level <= startup->log_level) {
				startup->event_lputs(startup->event_cbdata,level,line);
			}
741
		} else
deuce's avatar
deuce committed
742
			lprintf(level,"Node %d %s", sbbs->cfg.node_num, line);
743
		JS_RESUMEREQUEST(cx, rc);
744
	}
rswindell's avatar
rswindell committed
745
746
	if(line != NULL)
		free(line);
747

748
	if(str==NULL)
749
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
750
	else
751
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
752
753
754
    return(JS_TRUE);
}

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

764
765
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

777
	rc=JS_SUSPENDREQUEST(cx);
778
	len=RingBufRead(&sbbs->inbuf,buf,len);
779
	JS_RESUMEREQUEST(cx, rc);
780
781

	if(len>0)
782
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len)));
783
784
785
786
787
788

	free(buf);
	return(JS_TRUE);
}

static JSBool
789
js_readln(JSContext *cx, uintN argc, jsval *arglist)
790
{
791
	jsval *argv=JS_ARGV(cx, arglist);
792
793
794
	char*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
795
	jsrefcount	rc;
796

797
798
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

802
803
804
805
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
806
807
808
809

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

810
	rc=JS_SUSPENDREQUEST(cx);
811
	len=sbbs->getstr(buf,len,K_NONE);
812
	JS_RESUMEREQUEST(cx, rc);
813
814

	if(len>0)
815
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf)));
816
817
818
819
820

	free(buf);
	return(JS_TRUE);
}

821
static JSBool
822
js_write(JSContext *cx, uintN argc, jsval *arglist)
823
{
824
	jsval *argv=JS_ARGV(cx, arglist);
825
    uintN		i;
826
    JSString*	str=NULL;
827
	sbbs_t*		sbbs;
828
	jsrefcount	rc;
829
830
	char		*cstr=NULL;
	size_t		cstr_sz=0;
831

832
833
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

834
835
836
837
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
838
839
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			return(JS_FALSE);
deuce's avatar
deuce committed
840
		JSSTRING_TO_RASTRING(cx, str, cstr, &cstr_sz, NULL);
deuce's avatar
deuce committed
841
		if(cstr==NULL)
842
		    return(JS_FALSE);
843
		rc=JS_SUSPENDREQUEST(cx);
844
		if(sbbs->online==ON_LOCAL)
deuce's avatar
deuce committed
845
			eprintf(LOG_INFO,"%s",cstr);
846
		else
deuce's avatar
deuce committed
847
			sbbs->bputs(cstr);
848
		JS_RESUMEREQUEST(cx, rc);
849
	}
rswindell's avatar
rswindell committed
850
	FREE_AND_NULL(cstr);
851
852

	if(str==NULL)
853
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
854
	else
855
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
856
857
858
    return(JS_TRUE);
}

859
static JSBool
860
js_write_raw(JSContext *cx, uintN argc, jsval *arglist)
861
{
862
	jsval *argv=JS_ARGV(cx, arglist);
863
    uintN		i;
deuce's avatar
deuce committed
864
865
    char*		str=NULL;
    size_t		str_sz=0;
866
867
	size_t		len;
	sbbs_t*		sbbs;
868
	jsrefcount	rc;
869

870
871
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

872
873
874
875
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
876
		JSVALUE_TO_RASTRING(cx, argv[i], str, &str_sz, &len);
877
		if(str==NULL)
878
		    return(JS_FALSE);
879
		rc=JS_SUSPENDREQUEST(cx);
880
		sbbs->putcom(str, len);
881
		JS_RESUMEREQUEST(cx, rc);
882
	}
rswindell's avatar
rswindell committed
883
884
	if (str != NULL)
		free(str);
885
886
887
888

    return(JS_TRUE);
}

889
static JSBool
890
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
891
892
{
	sbbs_t*		sbbs;
893
	jsrefcount	rc;
894

895
896
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

900
	js_write(cx,argc,arglist);
901
	rc=JS_SUSPENDREQUEST(cx);
902
903
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);
904
	JS_RESUMEREQUEST(cx, rc);
905
906
907
908
909

    return(JS_TRUE);
}

static JSBool
910
js_printf(JSContext *cx, uintN argc, jsval *arglist)
911
{
912
	jsval *argv=JS_ARGV(cx, arglist);
913
914
	char*		p;
	sbbs_t*		sbbs;
915
	jsrefcount	rc;
916

917
918
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

922
923
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
924
925
926
		return(JS_FALSE);
	}

927
	rc=JS_SUSPENDREQUEST(cx);
928
	if(sbbs->online==ON_LOCAL)
929
		eprintf(LOG_INFO,"%s",p);
930
931
	else
		sbbs->bputs(p);
932
	JS_RESUMEREQUEST(cx, rc);
933

934
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
935

936
	js_sprintf_free(p);
937
938
939
940
941

    return(JS_TRUE);
}

static JSBool
942
js_alert(JSContext *cx, uintN argc, jsval *arglist)
943
{
944
	jsval *argv=JS_ARGV(cx, arglist);
945
	sbbs_t*		sbbs;
946
	jsrefcount	rc;
deuce's avatar
deuce committed
947
	char		*cstr;
948

949
950
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

deuce's avatar
deuce committed
954
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
955
	if(cstr==NULL)
956
957
	    return(JS_FALSE);

958
	rc=JS_SUSPENDREQUEST(cx);
959
960
961
962
963
964
965
966
	if(sbbs->online==ON_LOCAL)
		eprintf(LOG_WARNING, "%s", cstr);
	else {
		sbbs->attr(sbbs->cfg.color[clr_err]);
		sbbs->bputs(cstr);
		sbbs->attr(LIGHTGRAY);
		sbbs->bputs(crlf);
	}
deuce's avatar
deuce committed
967
	free(cstr);
968
	JS_RESUMEREQUEST(cx, rc);
969

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

972
973
974
975
    return(JS_TRUE);
}

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

983
984
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

992
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
993
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->yesno(cstr)));
deuce's avatar
deuce committed
994
	free(cstr);
995
	JS_RESUMEREQUEST(cx, rc);
996
997
998
	return(JS_TRUE);
}

999
static JSBool
1000
js_deny(JSContext *cx, uintN argc, jsval *arglist)
1001
{
1002
	jsval *argv=JS_ARGV(cx, arglist);
1003
1004
	sbbs_t*		sbbs;
	jsrefcount	rc;
deuce's avatar
deuce committed
1005
	char		*cstr;
1006

1007
1008
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

deuce's avatar
deuce committed
1012
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
1013
	if(cstr==NULL)
1014
1015
1016
	    return(JS_FALSE);

	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
1017
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->noyes(cstr)));
deuce's avatar
deuce committed
1018
	free(cstr);
1019
1020
1021
1022
1023
	JS_RESUMEREQUEST(cx, rc);
	return(JS_TRUE);
}


1024
static JSBool
1025
js_prompt(JSContext *cx, uintN argc, jsval *arglist)
1026
{
1027
	jsval *argv=JS_ARGV(cx, arglist);
1028
1029
1030
	char		instr[81];
    JSString *	str;
	sbbs_t*		sbbs;
1031
	jsrefcount	rc;
1032
    char*		prompt=NULL;
1033

1034
1035
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

1039
1040
1041
1042
1043
	if(argc) {
		JSVALUE_TO_MSTRING(cx, argv[0], prompt, NULL);
		if(prompt==NULL)
			return(JS_FALSE);
	}
1044
1045

	if(argc>1) {
deuce's avatar
deuce committed
1046
		JSVALUE_TO_STRBUF(cx, argv[1], instr, sizeof(instr), NULL);
1047
1048
1049
	} else
		instr[0]=0;

1050
	rc=JS_SUSPENDREQUEST(cx);
1051
1052
1053
1054
	if(prompt != NULL) {
		sbbs->bprintf("\1n\1y\1h%s\1w: ",prompt);
		free(prompt);
	}
1055
1056

	if(!sbbs->getstr(instr,sizeof(instr)-1,K_EDIT)) {
1057
		JS_SET_RVAL(cx, arglist, JSVAL_NULL);
1058
		JS_RESUMEREQUEST(cx, rc);
1059
1060
		return(JS_TRUE);
	}
1061
	JS_RESUMEREQUEST(cx, rc);
1062
1063
1064
1065

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

1066
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
1067
1068
1069
    return(JS_TRUE);
}

1070
static jsSyncMethodSpec js_global_functions[] = {
1071
	{"log",				js_log,				1,	JSTYPE_STRING,	JSDOCSTR("[level,] value [,value]")
1072
	,JSDOCSTR("add a line of text to the server and/or system log, "
1073
1074
		"<i>values</i> are typically string constants or variables, "
		"<i>level</i> is the debug level/priority (default: <tt>LOG_INFO</tt>)")
1075
	,311
1076
	},
1077
1078
	{"read",			js_read,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read up to count characters from input stream")
1079
	,311
1080
1081
1082
	},
	{"readln",			js_readln,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read a single line, up to count characters, from input stream")
1083
	,311
1084
	},
1085
	{"write",			js_write,			0,	JSTYPE_VOID,	JSDOCSTR("value [,value]")
1086
	,JSDOCSTR("send one or more values (typically strings) to the server output")
1087
	,311
1088
	},
1089
1090
	{"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
1091
	,314
1092
	},
1093
1094
1095
1096
	{"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)")
1097
	,311
1098
	},
1099
    {"printf",          js_printf,          1,	JSTYPE_STRING,	JSDOCSTR("string format [,value][,value]")
1100
	,JSDOCSTR("print a formatted string - <small>CAUTION: for experienced C programmers ONLY</small>")
1101
	,310
1102
1103
1104
	},	
	{"alert",			js_alert,			1,	JSTYPE_VOID,	JSDOCSTR("value")
	,JSDOCSTR("print an alert message (ala client-side JS)")
1105
	,310
1106
1107
1108
	},
	{"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)")
1109
	,310
1110
1111
1112
	},
	{"confirm",			js_confirm,			1,	JSTYPE_BOOLEAN,	JSDOCSTR("value")
	,JSDOCSTR("displays a Yes/No prompt and returns <i>true</i> or <i>false</i> "
1113
		"based on user's confirmation (ala client-side JS, <i>true</i> = yes)")
1114
	,310
1115
	},
1116
1117
1118
1119
1120
	{"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
	},
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
    {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;
1131
	jsrefcount	rc;
1132
	int		log_level;
1133
	char	nodestr[128];
1134
1135
1136

	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return;
1137
1138
1139
1140
1141

    if(sbbs->cfg.node_num)
    	SAFEPRINTF(nodestr,"Node %d",sbbs->cfg.node_num);
    else
    	SAFECOPY(nodestr,sbbs->client_name);
1142
1143
	
	if(report==NULL) {
1144
		lprintf(LOG_ERR,"%s !JavaScript: %s", nodestr, message);