main.cpp 161 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
static	link_list_t connections;
101
102
103
#ifdef _THREAD_SUID_BROKEN
int	thread_suid_broken=TRUE;			/* NPTL is no longer broken */
#endif
104
105
106
107
108

extern "C" {

static bbs_startup_t* startup=NULL;

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

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

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

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

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

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

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

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

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

164
    return(startup->lputs(startup->cbdata,level,str));
165
166
}

167
int lprintf(int level, const char *fmt, ...)
168
169
170
171
172
173
174
175
{
	va_list argptr;
	char sbuf[1024];

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

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

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

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

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

deuce's avatar
deuce committed
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
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);
}

226
SOCKET open_socket(int type, const char* protocol)
227
228
229
230
231
232
{
	SOCKET	sock;
	char	error[256];

	sock=socket(AF_INET, type, IPPROTO_IP);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
233
		startup->socket_open(startup->cbdata,TRUE);
234
	if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
235
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
236
237
238
239

	return(sock);
}

deuce's avatar
deuce committed
240
SOCKET accept_socket(SOCKET s, union xp_sockaddr* addr, socklen_t* addrlen)
241
242
243
{
	SOCKET	sock;

deuce's avatar
deuce committed
244
245
	sock=accept(s,&addr->addr,addrlen);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
246
		startup->socket_open(startup->cbdata,TRUE);
247
248
249
250
251
252
253
254
255
256
257
258
259

	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);
260
	if(startup!=NULL && startup->socket_open!=NULL)
261
		startup->socket_open(startup->cbdata,FALSE);
262
	if(result!=0 && ERROR_VALUE!=ENOTSOCK)
263
		lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
264
265
266
	return(result);
}

deuce's avatar
deuce committed
267
/* TODO: IPv6 */
268
269
270
271
272
273
u_long resolve_ip(char *addr)
{
	HOSTENT*	host;
	char*		p;

	if(*addr==0)
274
		return((u_long)INADDR_NONE);
275
276

	for(p=addr;*p;p++)
277
		if(*p!='.' && !isdigit((uchar)*p))
278
279
280
281
			break;
	if(!(*p))
		return(inet_addr(addr));
	if((host=gethostbyname(addr))==NULL) 
282
		return((u_long)INADDR_NONE);
283
284
285
286
287
	return(*((ulong*)host->h_addr_list[0]));
}

} /* extern "C" */

288
289
290
291
292
293
294
295
296
297
298
#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) {
299
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
300
301
302
303
		WSAInitialized=TRUE;
		return(TRUE);
	}

304
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
305
306
307
308
309
310
311
312
313
314
	return(FALSE);
}

#else /* No WINSOCK */

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

#endif

315
316
DLLEXPORT void DLLCALL sbbs_srand()
{
317
	DWORD seed;
318

319
	xp_randomize();
320
#if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
321
	int     rf,rd=0;
322

323
324
	if((rf=open(RANDOM_DEV, O_RDONLY|O_NONBLOCK))!=-1) {
		rd=read(rf, &seed, sizeof(seed));
325
326
		close(rf);
	}
deuce's avatar
deuce committed
327
	if (rd != sizeof(seed))
328
#endif
329
		seed = time32(NULL) ^ (uintmax_t)GetCurrentThreadId();
330
331

 	srand(seed);
332
333
334
	sbbs_random(10);	/* Throw away first number */
}

335
int DLLCALL sbbs_random(int n)
336
337
338
339
{
	return(xp_random(n));
}

340
341
#ifdef JAVASCRIPT

342
343
static js_server_props_t js_server_props;

344
345
346
347
348
349
350
351
352
353
354
355
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
356
357
		if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)	/* Assertion here, in _heap_alloc_dbg, June-21-2004 */
			return(JS_FALSE);								/* Caused by nntpservice.js? */
358

359
360
361
362
	if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
		,NULL,NULL,flags))
		return(JS_FALSE);

363
364
365
366
367
368
369
370
371
372
373
	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;
	}

374
	return(JS_TRUE);
375
376
}

377
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
378
379

JSBool
380
DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
381
382
383
384
385
386
{
	JSString* js_str = JS_NewStringCopyZ(cx, str);

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

rswindell's avatar
rswindell committed
387
388
389
	if(ver < 10000)		/* auto convert 313 to 31300 */
		ver*=100;

390
	return(JS_DefineProperty(cx,obj,"_description"
391
392
393
			,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
		&& JS_DefineProperty(cx,obj,"_ver"
			,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
394
395
396
}

JSBool
397
DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
398
399
400
401
402
403
404
405
406
407
{
	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));
}

408
#ifdef BUILD_JSDOCS
409
410

static const char* method_array_name = "_method_list";
411
static const char* propver_array_name = "_property_ver_list";
412
413
414
415
416
417
418
419
420
421
422
423
424

/*
 * 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
425
426
	"null",
	"xml",
427
428
	"array",
	"alias",
deuce's avatar
deuce committed
429
	"undefined"
430
431
};

432
433
434
435
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint		i;
436
	long		ver;
437
438
439
440
441
442
443
444
445
446
447
448
	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++) {
449
450
		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))
451
452
			return(JS_FALSE);
		if(props[i].flags&JSPROP_ENUMERATE) {	/* No need to version invisible props */
453
454
455
			if((ver=props[i].ver) < 10000)		/* auto convert 313 to 31300 */
				ver*=100;
			val = INT_TO_JSVAL(ver);
456
457
458
459
460
461
462
463
			if(!JS_SetElement(cx, array, len++, &val))
				return(JS_FALSE);
		}
	}

	return(JS_TRUE);
}

464
JSBool 
deuce's avatar
deuce committed
465
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
466
467
468
{
	int			i;
	jsuint		len=0;
469
	long		ver;
470
471
472
473
	jsval		val;
	JSObject*	method;
	JSObject*	method_array;
	JSString*	js_str;
deuce's avatar
deuce committed
474
475
	size_t		str_len=0;
	char		*str=NULL;
476
477

	/* Return existing method_list array if it's already been created */
deuce's avatar
deuce committed
478
	if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID) {
479
		method_array=JSVAL_TO_OBJECT(val);
deuce's avatar
deuce committed
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
		// 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 {
495
496
		if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL) 
			return(JS_FALSE);
deuce's avatar
deuce committed
497
498
		if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
				, NULL, NULL, 0))
499
			return(JS_FALSE);
deuce's avatar
deuce committed
500
	}
501
502
503

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

504
505
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
506
507
508
509

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

510
		method = JS_NewObject(cx, NULL, NULL, method_array);	/* exception here June-7-2003 */
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544

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

545
		if(funcs[i].ver) {
546
			if((ver=funcs[i].ver) < 10000)		/* auto convert 313 to 31300 */
547
548
				ver*=100;
			val = INT_TO_JSVAL(ver);
549
550
551
			JS_SetProperty(cx,method, "ver", &val);
		}

552
553
554
555
556
557
558
559
		val=OBJECT_TO_JSVAL(method);
		if(!JS_SetElement(cx, method_array, len+i, &val))
			return(JS_FALSE);
	}

	return(JS_TRUE);
}

560
561
562
563
564
/*
 * Always resolve all here since
 * 1) We'll always be enumerating anyways
 * 2) The speed penalty won't be seen in production code anyways
 */
565
566
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
567
568
569
{
	JSBool	ret=JS_TRUE;

deuce's avatar
deuce committed
570
	if(props) {
571
572
		if(!js_DefineSyncProperties(cx, obj, props))
			ret=JS_FALSE;
deuce's avatar
deuce committed
573
	}
574
		
deuce's avatar
deuce committed
575
	if(funcs) {
deuce's avatar
deuce committed
576
		if(!js_DefineSyncMethods(cx, obj, funcs))
577
			ret=JS_FALSE;
deuce's avatar
deuce committed
578
	}
579

deuce's avatar
deuce committed
580
581
582
583
	if(consts) {
		if(!js_DefineConstIntegers(cx, obj, consts, flags))
			ret=JS_FALSE;
	}
584

585
586
587
	return(ret);
}

588
#else // NON-JSDOCS
589

590
591
592
593
594
595
596
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint i;

	for(i=0;props[i].name;i++) 
		if(!JS_DefinePropertyWithTinyId(cx, obj, 
597
			props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
598
599
600
601
602
603
			return(JS_FALSE);

	return(JS_TRUE);
}


604
JSBool 
deuce's avatar
deuce committed
605
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
606
{
607
	uint i;
608
609

	for(i=0;funcs[i].name;i++)
610
611
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
612
613
614
	return(JS_TRUE);
}

615
616
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
617
618
{
	uint i;
619
	jsval	val;
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641

	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);
			}
		}
	}
642
	if(consts) {
deuce's avatar
deuce committed
643
		for(i=0;consts[i].name;i++) {
644
			if(name==NULL || strcmp(name, consts[i].name)==0) {
645
				val=INT_TO_JSVAL(consts[i].val);
646

deuce's avatar
deuce committed
647
				if(!JS_DefineProperty(cx, obj, consts[i].name, val ,NULL, NULL, flags))
648
649
650
651
652
653
654
					return(JS_FALSE);

				if(name)
					return(JS_TRUE);
			}
		}
	}
655
656
657
658

	return(JS_TRUE);
}

659
660
#endif

661
662
663
664
665
666
667
668
/* 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++) {
669
		val=INT_TO_JSVAL(ints[i].val);
670
671
672
673

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

675
676
677
	return(JS_TRUE);
}

678
static JSBool
679
js_log(JSContext *cx, uintN argc, jsval *arglist)
680
{
681
	jsval *argv=JS_ARGV(cx, arglist);
682
683
    uintN		i=0;
	int32		level=LOG_INFO;
684
    JSString*	str=NULL;
685
	sbbs_t*		sbbs;
686
	jsrefcount	rc;
deuce's avatar
deuce committed
687
688
	char		*line=NULL;
	size_t		line_sz=0;
689

690
691
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

695
696
697
698
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
699
700

    for(; i<argc; i++) {
deuce's avatar
deuce committed
701
702
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			return(JS_FALSE);
deuce's avatar
deuce committed
703
		JSSTRING_TO_RASTRING(cx, str, line, &line_sz, NULL);
deuce's avatar
deuce committed
704
		if(line==NULL)
705
		    return(JS_FALSE);
706
		rc=JS_SUSPENDREQUEST(cx);
707
		if(sbbs->online==ON_LOCAL) {
deuce's avatar
deuce committed
708
709
710
			if(startup!=NULL && startup->event_lputs!=NULL && level <= startup->log_level) {
				startup->event_lputs(startup->event_cbdata,level,line);
			}
711
		} else
deuce's avatar
deuce committed
712
			lprintf(level,"Node %d %s", sbbs->cfg.node_num, line);
713
		JS_RESUMEREQUEST(cx, rc);
714
	}
deuce's avatar
deuce committed
715
	free(line);
716

717
	if(str==NULL)
718
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
719
	else
720
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
721
722
723
    return(JS_TRUE);
}

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

733
734
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

746
	rc=JS_SUSPENDREQUEST(cx);
747
	len=RingBufRead(&sbbs->inbuf,buf,len);
748
	JS_RESUMEREQUEST(cx, rc);
749
750

	if(len>0)
751
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len)));
752
753
754
755
756
757

	free(buf);
	return(JS_TRUE);
}

static JSBool
758
js_readln(JSContext *cx, uintN argc, jsval *arglist)
759
{
760
	jsval *argv=JS_ARGV(cx, arglist);
761
762
763
	char*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
764
	jsrefcount	rc;
765

766
767
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

779
	rc=JS_SUSPENDREQUEST(cx);
780
	len=sbbs->getstr(buf,len,K_NONE);
781
	JS_RESUMEREQUEST(cx, rc);
782
783

	if(len>0)
784
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf)));
785
786
787
788
789

	free(buf);
	return(JS_TRUE);
}

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

801
802
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
807
808
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
			return(JS_FALSE);
deuce's avatar
deuce committed
809
		JSSTRING_TO_RASTRING(cx, str, cstr, &cstr_sz, NULL);
deuce's avatar
deuce committed
810
		if(cstr==NULL)
811
		    return(JS_FALSE);
812
		rc=JS_SUSPENDREQUEST(cx);
813
		if(sbbs->online==ON_LOCAL)
deuce's avatar
deuce committed
814
			eprintf(LOG_INFO,"%s",cstr);
815
		else
deuce's avatar
deuce committed
816
			sbbs->bputs(cstr);
817
		JS_RESUMEREQUEST(cx, rc);
818
819
820
	}

	if(str==NULL)
821
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
822
	else
823
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
824
825
826
    return(JS_TRUE);
}

827
static JSBool
828
js_write_raw(JSContext *cx, uintN argc, jsval *arglist)
829
{
830
	jsval *argv=JS_ARGV(cx, arglist);
831
    uintN		i;
deuce's avatar
deuce committed
832
833
    char*		str=NULL;
    size_t		str_sz=0;
834
835
	size_t		len;
	sbbs_t*		sbbs;
836
	jsrefcount	rc;
837

838
839
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

840
841
842
843
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
844
		JSVALUE_TO_RASTRING(cx, argv[i], str, &str_sz, &len);
845
		if(str==NULL)
846
		    return(JS_FALSE);
847
		rc=JS_SUSPENDREQUEST(cx);
848
		sbbs->putcom(str, len);
849
		JS_RESUMEREQUEST(cx, rc);
850
851
852
853
854
	}

    return(JS_TRUE);
}

855
static JSBool
856
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
857
858
{
	sbbs_t*		sbbs;
859
	jsrefcount	rc;
860

861
862
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

866
	js_write(cx,argc,arglist);
867
	rc=JS_SUSPENDREQUEST(cx);
868
869
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);
870
	JS_RESUMEREQUEST(cx, rc);
871
872
873
874
875

    return(JS_TRUE);
}

static JSBool
876
js_printf(JSContext *cx, uintN argc, jsval *arglist)
877
{
878
	jsval *argv=JS_ARGV(cx, arglist);
879
880
	char*		p;
	sbbs_t*		sbbs;
881
	jsrefcount	rc;
882

883
884
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

888
889
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
890
891
892
		return(JS_FALSE);
	}

893
	rc=JS_SUSPENDREQUEST(cx);
894
	if(sbbs->online==ON_LOCAL)
895
		eprintf(LOG_INFO,"%s",p);
896
897
	else
		sbbs->bputs(p);
898
	JS_RESUMEREQUEST(cx, rc);
899

900
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
901

902
	js_sprintf_free(p);
903
904
905
906
907

    return(JS_TRUE);
}

static JSBool
908
js_alert(JSContext *cx, uintN argc, jsval *arglist)
909
{
910
	jsval *argv=JS_ARGV(cx, arglist);
911
	sbbs_t*		sbbs;
912
	jsrefcount	rc;
deuce's avatar
deuce committed
913
	char		*cstr;
914

915
916
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

deuce's avatar
deuce committed
920
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
921
	if(cstr==NULL)
922
923
	    return(JS_FALSE);

924
	rc=JS_SUSPENDREQUEST(cx);
925
	sbbs->attr(sbbs->cfg.color[clr_err]);
deuce's avatar
deuce committed
926
	sbbs->bputs(cstr);
deuce's avatar
deuce committed
927
	free(cstr);
928
929
	sbbs->attr(LIGHTGRAY);
	sbbs->bputs(crlf);
930
	JS_RESUMEREQUEST(cx, rc);
931

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

934
935
936
937
    return(JS_TRUE);
}

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

945
946
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

954
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
955
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->yesno(cstr)));
deuce's avatar
deuce committed
956
	free(cstr);
957
	JS_RESUMEREQUEST(cx, rc);
958
959
960
	return(JS_TRUE);
}

961
static JSBool
962
js_deny(JSContext *cx, uintN argc, jsval *arglist)
963
{
964
	jsval *argv=JS_ARGV(cx, arglist);
965
966
	sbbs_t*		sbbs;
	jsrefcount	rc;
deuce's avatar
deuce committed
967
	char		*cstr;
968

969
970
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

deuce's avatar
deuce committed
974
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
975
	if(cstr==NULL)
976
977
978
	    return(JS_FALSE);

	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
979
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->noyes(cstr)));
deuce's avatar
deuce committed
980
	free(cstr);
981
982
983
984
985
	JS_RESUMEREQUEST(cx, rc);
	return(JS_TRUE);
}


986
static JSBool
987
js_prompt(JSContext *cx, uintN argc, jsval *arglist)
988
{
989
	jsval *argv=JS_ARGV(cx, arglist);
990
991
992
	char		instr[81];
    JSString *	str;
	sbbs_t*		sbbs;
993
	jsrefcount	rc;
994
    char*		prompt=NULL;
995

996
997
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

1001
1002
1003
1004
1005
	if(argc) {
		JSVALUE_TO_MSTRING(cx, argv[0], prompt, NULL);
		if(prompt==NULL)
			return(JS_FALSE);
	}
1006
1007

	if(argc>1) {
deuce's avatar
deuce committed
1008
		JSVALUE_TO_STRBUF(cx, argv[1], instr, sizeof(instr), NULL);
1009
1010
1011
	} else
		instr[0]=0;