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

745
	if(str==NULL)
746
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
747
	else
748
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
749
750
751
    return(JS_TRUE);
}

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

761
762
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

774
	rc=JS_SUSPENDREQUEST(cx);
775
	len=RingBufRead(&sbbs->inbuf,buf,len);
776
	JS_RESUMEREQUEST(cx, rc);
777
778

	if(len>0)
779
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len)));
780
781
782
783
784
785

	free(buf);
	return(JS_TRUE);
}

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

794
795
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

807
	rc=JS_SUSPENDREQUEST(cx);
808
	len=sbbs->getstr(buf,len,K_NONE);
809
	JS_RESUMEREQUEST(cx, rc);
810
811

	if(len>0)
812
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf)));
813
814
815
816
817

	free(buf);
	return(JS_TRUE);
}

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

829
830
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

831
832
833
834
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

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

	if(str==NULL)
849
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
850
	else
851
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
852
853
854
    return(JS_TRUE);
}

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

866
867
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

868
869
870
871
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
872
		JSVALUE_TO_RASTRING(cx, argv[i], str, &str_sz, &len);
873
		if(str==NULL)
874
		    return(JS_FALSE);
875
		rc=JS_SUSPENDREQUEST(cx);
876
		sbbs->putcom(str, len);
877
		JS_RESUMEREQUEST(cx, rc);
878
879
880
881
882
	}

    return(JS_TRUE);
}

883
static JSBool
884
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
885
886
{
	sbbs_t*		sbbs;
887
	jsrefcount	rc;
888

889
890
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

894
	js_write(cx,argc,arglist);
895
	rc=JS_SUSPENDREQUEST(cx);
896
897
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);
898
	JS_RESUMEREQUEST(cx, rc);
899
900
901
902
903

    return(JS_TRUE);
}

static JSBool
904
js_printf(JSContext *cx, uintN argc, jsval *arglist)
905
{
906
	jsval *argv=JS_ARGV(cx, arglist);
907
908
	char*		p;
	sbbs_t*		sbbs;
909
	jsrefcount	rc;
910

911
912
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

916
917
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
918
919
920
		return(JS_FALSE);
	}

921
	rc=JS_SUSPENDREQUEST(cx);
922
	if(sbbs->online==ON_LOCAL)
923
		eprintf(LOG_INFO,"%s",p);
924
925
	else
		sbbs->bputs(p);
926
	JS_RESUMEREQUEST(cx, rc);
927

928
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
929

930
	js_sprintf_free(p);
931
932
933
934
935

    return(JS_TRUE);
}

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

943
944
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

952
	rc=JS_SUSPENDREQUEST(cx);
953
954
955
956
957
958
959
960
	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
961
	free(cstr);
962
	JS_RESUMEREQUEST(cx, rc);
963

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

966
967
968
969
    return(JS_TRUE);
}

static JSBool
970
js_confirm(JSContext *cx, uintN argc, jsval *arglist)
971
{
972
	jsval *argv=JS_ARGV(cx, arglist);
973
	sbbs_t*		sbbs;
974
	jsrefcount	rc;
deuce's avatar
deuce committed
975
	char		*cstr;
976

977
978
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

deuce's avatar
deuce committed
982
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
983
	if(cstr==NULL)
984
985
	    return(JS_FALSE);

986
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
987
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->yesno(cstr)));
deuce's avatar
deuce committed
988
	free(cstr);
989
	JS_RESUMEREQUEST(cx, rc);
990
991
992
	return(JS_TRUE);
}

993
static JSBool
994
js_deny(JSContext *cx, uintN argc, jsval *arglist)
995
{
996
	jsval *argv=JS_ARGV(cx, arglist);
997
998
	sbbs_t*		sbbs;
	jsrefcount	rc;
deuce's avatar
deuce committed
999
	char		*cstr;
1000

1001
1002
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

deuce's avatar
deuce committed
1006
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
1007
	if(cstr==NULL)
1008
1009
1010
	    return(JS_FALSE);

	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
1011
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->noyes(cstr)));
deuce's avatar
deuce committed
1012
	free(cstr);
1013
1014
1015
1016
1017
	JS_RESUMEREQUEST(cx, rc);
	return(JS_TRUE);
}


1018
static JSBool
1019
js_prompt(JSContext *cx, uintN argc, jsval *arglist)
1020
{
1021
	jsval *argv=JS_ARGV(cx, arglist);
1022
1023
1024
	char		instr[81];
    JSString *	str;
	sbbs_t*		sbbs;
1025
	jsrefcount	rc;
1026
    char*		prompt=NULL;