main.cpp 162 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
int lprintf(int level, const char *fmt, ...)
170
171
172
173
174
175
176
177
{
	va_list argptr;
	char sbuf[1024];

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

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

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

	if(level <= LOG_ERR) {
		errorlog(&scfg,startup==NULL ? NULL:startup->host_name, sbuf);
		if(startup!=NULL && startup->errormsg!=NULL)
			startup->errormsg(startup->cbdata,level,sbuf);
	}

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

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

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

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

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

	return(sock);
}

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

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

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

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

	if(*addr==0)
276
		return((u_long)INADDR_NONE);
277
278

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

} /* extern "C" */

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

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

#else /* No WINSOCK */

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

#endif

317
318
DLLEXPORT void DLLCALL sbbs_srand()
{
319
	DWORD seed;
320

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

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

 	srand(seed);
334
335
336
	sbbs_random(10);	/* Throw away first number */
}

337
int DLLCALL sbbs_random(int n)
338
339
340
341
{
	return(xp_random(n));
}

342
343
#ifdef JAVASCRIPT

344
345
static js_server_props_t js_server_props;

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

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

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

376
	return(JS_TRUE);
377
378
}

379
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
380
381

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

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

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

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

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

410
#ifdef BUILD_JSDOCS
411
412

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

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

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

	return(JS_TRUE);
}

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

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

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

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

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

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

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

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

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

	return(JS_TRUE);
}

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

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

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

587
588
589
	return(ret);
}

590
#else // NON-JSDOCS
591

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

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

	return(JS_TRUE);
}


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

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

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

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

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

				if(name)
					return(JS_TRUE);
			}
		}
	}
657
658
659
660

	return(JS_TRUE);
}

661
662
#endif

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

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

677
678
679
	return(JS_TRUE);
}

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

692
693
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

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

735
736
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

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

	free(buf);
	return(JS_TRUE);
}

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

768
769
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

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

	free(buf);
	return(JS_TRUE);
}

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

803
804
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

805
806
807
808
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

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

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

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

840
841
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

842
843
844
845
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

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

    return(JS_TRUE);
}

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

863
864
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

    return(JS_TRUE);
}

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

885
886
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

902
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
903

904
	js_sprintf_free(p);
905
906
907
908
909

    return(JS_TRUE);
}

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

917
918
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

936
937
938
939
    return(JS_TRUE);
}

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

947
948
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

971
972
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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


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

998
999
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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