main.cpp 166 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
deuce's avatar
deuce committed
74
75
76
77
78
79
80
	#define SSH_END() do {                                  \
		if(ssh) {                                       \
			pthread_mutex_lock(&sbbs->ssh_mutex);   \
			cryptDestroySession(sbbs->ssh_session); \
			pthread_mutex_unlock(&sbbs->ssh_mutex); \
		}                                               \
	} while(0)
81
82
83
84
#else
	#define	SSH_END()
#endif

85
86
volatile time_t	uptime=0;
volatile ulong	served=0;
87

88
static	protected_uint32_t node_threads_running;
89
90
91
92
93
94
95
96
		
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
97
struct xpms_set				*ts_set;
98
99
100
101
102
static	sbbs_t*	sbbs=NULL;
static	scfg_t	scfg;
static	char *	text[TOTAL_TEXT];
static	WORD	first_node;
static	WORD	last_node;
103
static	bool	terminate_server=false;
104
105
static	str_list_t recycle_semfiles;
static	str_list_t shutdown_semfiles;
106
107
static	link_list_t current_logins;
static	link_list_t current_connections;
108
109
110
#ifdef _THREAD_SUID_BROKEN
int	thread_suid_broken=TRUE;			/* NPTL is no longer broken */
#endif
111

deuce's avatar
deuce committed
112
113
114
115
116
#define GCES(status, node, sess, action) do {                                  \
	char *GCES_estr;                                                       \
	int GCES_level;                                                        \
	get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level); \
	if (GCES_estr) {                                                       \
117
		lprintf(GCES_level, "Node %d SSH %s", node, GCES_estr);        \
118
		free_crypt_attrstr(GCES_estr);                                               \
deuce's avatar
deuce committed
119
120
121
122
123
124
125
126
	}                                                                      \
} while (0)

#define GCESNN(status, sess, action) do {                                      \
	char *GCES_estr;                                                       \
	int GCES_level;                                                        \
	get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level); \
	if (GCES_estr) {                                                       \
127
		lprintf(GCES_level, "SSH %s", GCES_estr);                      \
128
		free_crypt_attrstr(GCES_estr);                                               \
deuce's avatar
deuce committed
129
130
131
132
133
134
135
136
	}                                                                      \
} while (0)

#define GCESS(status, sock, sess, action) do {                                 \
	char *GCES_estr;                                                       \
	int GCES_level;                                                        \
	get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level); \
	if (GCES_estr) {                                                       \
137
		lprintf(GCES_level, "%04d SSH %s", sock, GCES_estr);           \
138
		free_crypt_attrstr(GCES_estr);                                               \
deuce's avatar
deuce committed
139
140
141
	}                                                                      \
} while (0)

142
143
144
145
extern "C" {

static bbs_startup_t* startup=NULL;

rswindell's avatar
rswindell committed
146
static const char* status(const char* str)
147
148
{
	if(startup!=NULL && startup->status!=NULL)
149
	    startup->status(startup->cbdata,str);
rswindell's avatar
rswindell committed
150
	return str;
151
152
153
154
155
}

static void update_clients()
{
	if(startup!=NULL && startup->clients!=NULL)
156
		startup->clients(startup->cbdata,protected_uint32_value(node_threads_running));
157
158
159
160
}

void client_on(SOCKET sock, client_t* client, BOOL update)
{
161
162
	if(!update)
		listAddNodeData(&current_connections, client->addr, strlen(client->addr)+1, sock, LAST_NODE);
163
	if(startup!=NULL && startup->client_on!=NULL)
164
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
165
166
167
168
}

static void client_off(SOCKET sock)
{
169
	listRemoveTaggedNode(&current_connections, sock, /* free_data */TRUE);
170
	if(startup!=NULL && startup->client_on!=NULL)
171
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
172
173
174
175
176
}

static void thread_up(BOOL setuid)
{
	if(startup!=NULL && startup->thread_up!=NULL)
177
		startup->thread_up(startup->cbdata,TRUE,setuid);
178
179
180
181
182
}

static void thread_down()
{
	if(startup!=NULL && startup->thread_up!=NULL)
183
		startup->thread_up(startup->cbdata,FALSE,FALSE);
184
185
}

186
int lputs(int level, const char* str)
187
{
188
	if(level <= LOG_ERR) {
189
		errorlog(&scfg,startup==NULL ? NULL:startup->host_name, str);
190
191
192
		if(startup!=NULL && startup->errormsg!=NULL)
			startup->errormsg(startup->cbdata,level,str);
	}
193

194
	if(startup==NULL || startup->lputs==NULL || str==NULL || level > startup->log_level)
195
196
    	return(0);

197
198
199
200
201
#if defined(_WIN32)
	if(IsBadCodePtr((FARPROC)startup->lputs))
		return(0);
#endif

202
    return(startup->lputs(startup->cbdata,level,str));
203
204
}

205
206
int eputs(int level, const char *str)
{
207
208
209
	if(*str == 0)
		return 0;

210
211
212
213
214
215
216
217
218
219
220
221
	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));
}

222
int lprintf(int level, const char *fmt, ...)
223
224
225
226
227
228
229
230
{
	va_list argptr;
	char sbuf[1024];

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

234
int eprintf(int level, const char *fmt, ...)
235
236
237
238
239
240
241
242
{
	va_list argptr;
	char sbuf[1024];

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

244
	strip_ctrl(sbuf, sbuf);
245

246
    return(eputs(level,truncsp(sbuf)));
247
}
248

249
250
251
252
253
254
/* 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);
255
256
}

257
258
259
260
261
262
263
264
265
266
267
268
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
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
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);
}

293
SOCKET open_socket(int type, const char* protocol)
294
295
296
297
298
299
{
	SOCKET	sock;
	char	error[256];

	sock=socket(AF_INET, type, IPPROTO_IP);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
300
		startup->socket_open(startup->cbdata,TRUE);
301
	if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
302
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
303
304
305
306

	return(sock);
}

307
// Used by sbbs_t::ftp_put():
deuce's avatar
deuce committed
308
SOCKET accept_socket(SOCKET s, union xp_sockaddr* addr, socklen_t* addrlen)
309
310
311
{
	SOCKET	sock;

deuce's avatar
deuce committed
312
313
	sock=accept(s,&addr->addr,addrlen);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
314
		startup->socket_open(startup->cbdata,TRUE);
315
316
317
318
319
320
321
322
323
324
325
326
327

	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);
328
	if(startup!=NULL && startup->socket_open!=NULL)
329
		startup->socket_open(startup->cbdata,FALSE);
330
	if(result!=0 && ERROR_VALUE!=ENOTSOCK)
331
		lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
332
333
334
	return(result);
}

deuce's avatar
deuce committed
335
/* TODO: IPv6 */
336
337
338
339
340
341
u_long resolve_ip(char *addr)
{
	HOSTENT*	host;
	char*		p;

	if(*addr==0)
342
		return((u_long)INADDR_NONE);
343
344

	for(p=addr;*p;p++)
345
		if(*p!='.' && !isdigit((uchar)*p))
346
347
348
349
			break;
	if(!(*p))
		return(inet_addr(addr));
	if((host=gethostbyname(addr))==NULL) 
350
		return((u_long)INADDR_NONE);
351
352
353
354
355
	return(*((ulong*)host->h_addr_list[0]));
}

} /* extern "C" */

356
357
358
359
360
361
362
363
364
365
366
#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) {
367
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
368
369
370
371
		WSAInitialized=TRUE;
		return(TRUE);
	}

372
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
373
374
375
376
377
378
379
380
381
382
	return(FALSE);
}

#else /* No WINSOCK */

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

#endif

383
384
DLLEXPORT void DLLCALL sbbs_srand()
{
385
	DWORD seed;
386

387
	xp_randomize();
388
#if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
389
	int     rf,rd=0;
390

391
392
	if((rf=open(RANDOM_DEV, O_RDONLY|O_NONBLOCK))!=-1) {
		rd=read(rf, &seed, sizeof(seed));
393
394
		close(rf);
	}
deuce's avatar
deuce committed
395
	if (rd != sizeof(seed))
396
#endif
397
		seed = time32(NULL) ^ (uintmax_t)GetCurrentThreadId();
398
399

 	srand(seed);
400
401
402
	sbbs_random(10);	/* Throw away first number */
}

403
int DLLCALL sbbs_random(int n)
404
405
406
407
{
	return(xp_random(n));
}

408
409
#ifdef JAVASCRIPT

410
411
static js_server_props_t js_server_props;

412
413
414
415
416
417
418
419
420
421
422
423
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
424
425
		if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)	/* Assertion here, in _heap_alloc_dbg, June-21-2004 */
			return(JS_FALSE);								/* Caused by nntpservice.js? */
426

427
428
429
430
	if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
		,NULL,NULL,flags))
		return(JS_FALSE);

431
432
433
434
435
436
437
438
439
440
441
	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;
	}

442
	return(JS_TRUE);
443
444
}

445
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
446
447

JSBool
448
DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
449
450
451
452
453
454
{
	JSString* js_str = JS_NewStringCopyZ(cx, str);

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

rswindell's avatar
rswindell committed
455
456
457
	if(ver < 10000)		/* auto convert 313 to 31300 */
		ver*=100;

458
	return(JS_DefineProperty(cx,obj,"_description"
459
460
461
			,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
		&& JS_DefineProperty(cx,obj,"_ver"
			,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
462
463
464
}

JSBool
465
DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
466
467
468
469
470
471
472
473
474
475
{
	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));
}

476
#ifdef BUILD_JSDOCS
477
478

static const char* method_array_name = "_method_list";
479
static const char* propver_array_name = "_property_ver_list";
480
481
482
483
484
485
486
487
488
489
490
491
492

/*
 * 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
493
494
	"null",
	"xml",
495
496
	"array",
	"alias",
deuce's avatar
deuce committed
497
	"undefined"
498
499
};

500
501
502
503
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint		i;
504
	long		ver;
505
506
507
508
509
510
511
512
513
514
515
516
	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++) {
517
518
		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))
519
520
			return(JS_FALSE);
		if(props[i].flags&JSPROP_ENUMERATE) {	/* No need to version invisible props */
521
522
523
			if((ver=props[i].ver) < 10000)		/* auto convert 313 to 31300 */
				ver*=100;
			val = INT_TO_JSVAL(ver);
524
525
526
527
528
529
530
531
			if(!JS_SetElement(cx, array, len++, &val))
				return(JS_FALSE);
		}
	}

	return(JS_TRUE);
}

532
JSBool 
deuce's avatar
deuce committed
533
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
534
535
536
{
	int			i;
	jsuint		len=0;
537
	long		ver;
538
539
540
541
	jsval		val;
	JSObject*	method;
	JSObject*	method_array;
	JSString*	js_str;
deuce's avatar
deuce committed
542
543
	size_t		str_len=0;
	char		*str=NULL;
544
545

	/* Return existing method_list array if it's already been created */
deuce's avatar
deuce committed
546
	if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID) {
547
		method_array=JSVAL_TO_OBJECT(val);
deuce's avatar
deuce committed
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
		// 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 {
563
564
		if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL) 
			return(JS_FALSE);
deuce's avatar
deuce committed
565
566
		if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
				, NULL, NULL, 0))
567
			return(JS_FALSE);
deuce's avatar
deuce committed
568
	}
569
570
571

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

572
573
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
574
575
576
577

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

578
		method = JS_NewObject(cx, NULL, NULL, method_array);	/* exception here June-7-2003 */
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612

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

613
		if(funcs[i].ver) {
614
			if((ver=funcs[i].ver) < 10000)		/* auto convert 313 to 31300 */
615
616
				ver*=100;
			val = INT_TO_JSVAL(ver);
617
618
619
			JS_SetProperty(cx,method, "ver", &val);
		}

620
621
622
623
624
625
626
627
		val=OBJECT_TO_JSVAL(method);
		if(!JS_SetElement(cx, method_array, len+i, &val))
			return(JS_FALSE);
	}

	return(JS_TRUE);
}

628
629
630
631
632
/*
 * Always resolve all here since
 * 1) We'll always be enumerating anyways
 * 2) The speed penalty won't be seen in production code anyways
 */
633
634
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
635
636
637
{
	JSBool	ret=JS_TRUE;

deuce's avatar
deuce committed
638
	if(props) {
639
640
		if(!js_DefineSyncProperties(cx, obj, props))
			ret=JS_FALSE;
deuce's avatar
deuce committed
641
	}
642
		
deuce's avatar
deuce committed
643
	if(funcs) {
deuce's avatar
deuce committed
644
		if(!js_DefineSyncMethods(cx, obj, funcs))
645
			ret=JS_FALSE;
deuce's avatar
deuce committed
646
	}
647

deuce's avatar
deuce committed
648
649
650
651
	if(consts) {
		if(!js_DefineConstIntegers(cx, obj, consts, flags))
			ret=JS_FALSE;
	}
652

653
654
655
	return(ret);
}

656
#else // NON-JSDOCS
657

658
659
660
661
662
663
664
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint i;

	for(i=0;props[i].name;i++) 
		if(!JS_DefinePropertyWithTinyId(cx, obj, 
665
			props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
666
667
668
669
670
671
			return(JS_FALSE);

	return(JS_TRUE);
}


672
JSBool 
deuce's avatar
deuce committed
673
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
674
{
675
	uint i;
676
677

	for(i=0;funcs[i].name;i++)
678
679
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
680
681
682
	return(JS_TRUE);
}

683
684
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
685
686
{
	uint i;
687
	jsval	val;
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709

	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);
			}
		}
	}
710
	if(consts) {
deuce's avatar
deuce committed
711
		for(i=0;consts[i].name;i++) {
712
			if(name==NULL || strcmp(name, consts[i].name)==0) {
713
				val=INT_TO_JSVAL(consts[i].val);
714

deuce's avatar
deuce committed
715
				if(!JS_DefineProperty(cx, obj, consts[i].name, val ,NULL, NULL, flags))
716
717
718
719
720
721
722
					return(JS_FALSE);

				if(name)
					return(JS_TRUE);
			}
		}
	}
723
724
725
726

	return(JS_TRUE);
}

727
728
#endif

729
730
731
732
733
734
735
736
/* 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++) {
737
		val=INT_TO_JSVAL(ints[i].val);
738
739
740
741

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

743
744
745
	return(JS_TRUE);
}

746
static JSBool
747
js_log(JSContext *cx, uintN argc, jsval *arglist)
748
{
749
	jsval *argv=JS_ARGV(cx, arglist);
750
751
    uintN		i=0;
	int32		level=LOG_INFO;
752
    JSString*	str=NULL;
753
	sbbs_t*		sbbs;
754
	jsrefcount	rc;
deuce's avatar
deuce committed
755
756
	char		*line=NULL;
	size_t		line_sz=0;
757

758
759
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

763
764
765
766
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
767
768

    for(; i<argc; i++) {
rswindell's avatar
rswindell committed
769
770
		if((str=JS_ValueToString(cx, argv[i]))==NULL) {
			FREE_AND_NULL(line);
deuce's avatar
deuce committed
771
			return(JS_FALSE);
rswindell's avatar
rswindell committed
772
		}
deuce's avatar
deuce committed
773
		JSSTRING_TO_RASTRING(cx, str, line, &line_sz, NULL);
deuce's avatar
deuce committed
774
		if(line==NULL)
775
		    return(JS_FALSE);
776
		rc=JS_SUSPENDREQUEST(cx);
777
		if(sbbs->online==ON_LOCAL) {
deuce's avatar
deuce committed
778
779
780
			if(startup!=NULL && startup->event_lputs!=NULL && level <= startup->log_level) {
				startup->event_lputs(startup->event_cbdata,level,line);
			}
781
		} else
deuce's avatar
deuce committed
782
			lprintf(level,"Node %d %s", sbbs->cfg.node_num, line);
783
		JS_RESUMEREQUEST(cx, rc);
784
	}
rswindell's avatar
rswindell committed
785
786
	if(line != NULL)
		free(line);
787

788
	if(str==NULL)
789
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
790
	else
791
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
792
793
794
    return(JS_TRUE);
}

795
static JSBool
796
js_read(JSContext *cx, uintN argc, jsval *arglist)
797
{
798
	jsval *argv=JS_ARGV(cx, arglist);
799
800
801
	uchar*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
802
	jsrefcount	rc;
803

804
805
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

809
810
811
812
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
813
814
815
816

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

817
	rc=JS_SUSPENDREQUEST(cx);
818
	len=RingBufRead(&sbbs->inbuf,buf,len);
819
	JS_RESUMEREQUEST(cx, rc);
820
821

	if(len>0)
822
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len)));
823
824
825
826
827
828

	free(buf);
	return(JS_TRUE);
}

static JSBool
829
js_readln(JSContext *cx, uintN argc, jsval *arglist)
830
{
831
	jsval *argv=JS_ARGV(cx, arglist);
832
833
834
	char*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
835
	jsrefcount	rc;
836

837
838
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

842
843
844
845
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
846
847
848
849

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

850
	rc=JS_SUSPENDREQUEST(cx);
851
	len=sbbs->getstr(buf,len,K_NONE);
852
	JS_RESUMEREQUEST(cx, rc);
853
854

	if(len>0)
855
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf)));
856
857
858
859
860

	free(buf);
	return(JS_TRUE);
}

861
static JSBool
862
js_write(JSContext *cx, uintN argc, jsval *arglist)
863
{
864
	jsval *argv=JS_ARGV(cx, arglist);
865
    uintN		i;
866
    JSString*	str=NULL;
867
	sbbs_t*		sbbs;
868
	jsrefcount	rc;
869
870
	char		*cstr=NULL;
	size_t		cstr_sz=0;
871

872
873
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

874
875
876
877
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
878
879
		if((str=JS_ValueToString(cx, argv[i]))==NULL) {
			FREE_AND_NULL(cstr);
deuce's avatar
deuce committed
880
			return(JS_FALSE);
881
		}
deuce's avatar
deuce committed
882
		JSSTRING_TO_RASTRING(cx, str, cstr, &cstr_sz, NULL);
deuce's avatar
deuce committed
883
		if(cstr==NULL)
884
		    return(JS_FALSE);
885
		rc=JS_SUSPENDREQUEST(cx);
886
		if(sbbs->online==ON_LOCAL)
deuce's avatar
deuce committed
887
			eprintf(LOG_INFO,"%s",cstr);
888
		else
deuce's avatar
deuce committed
889
			sbbs->bputs(cstr);
890
		JS_RESUMEREQUEST(cx, rc);
891
	}
rswindell's avatar
rswindell committed
892
	FREE_AND_NULL(cstr);
893
894

	if(str==NULL)
895
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
896
	else
897
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
898
899
900
    return(JS_TRUE);
}

901
static JSBool
902
js_write_raw(JSContext *cx, uintN argc, jsval *arglist)
903
{
904
	jsval *argv=JS_ARGV(cx, arglist);
905
    uintN		i;
deuce's avatar
deuce committed
906
907
    char*		str=NULL;
    size_t		str_sz=0;
908
909
	size_t		len;
	sbbs_t*		sbbs;
910
	jsrefcount	rc;
911

912
913
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
918
		JSVALUE_TO_RASTRING(cx, argv[i], str, &str_sz, &len);
919
		if(str==NULL)
920
		    return(JS_FALSE);
921
922
		if(len < 1)
			continue;
923
		rc=JS_SUSPENDREQUEST(cx);
924
		sbbs->putcom(str, len);
925
		JS_RESUMEREQUEST(cx, rc);
926
	}
rswindell's avatar
rswindell committed
927
928
	if (str != NULL)
		free(str);
929
930
931
932

    return(JS_TRUE);
}

933
static JSBool
934
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
935
936
{
	sbbs_t*		sbbs;
937
	jsrefcount	rc;
938

939
940
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

944
	js_write(cx,argc,arglist);
945
	rc=JS_SUSPENDREQUEST(cx);
946
947
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);
948
	JS_RESUMEREQUEST(cx, rc);
949
950
951
952
953

    return(JS_TRUE);
}

static JSBool
954
js_printf(JSContext *cx, uintN argc, jsval *arglist)
955
{
956
	jsval *argv=JS_ARGV(cx, arglist);
957
958
	char*		p;
	sbbs_t*		sbbs;
959
	jsrefcount	rc;
960

961
962
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

966
967
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
968
969
970
		return(JS_FALSE);
	}

971
	rc=JS_SUSPENDREQUEST(cx);
972
	if(sbbs->online==ON_LOCAL)
973
		eprintf(LOG_INFO,"%s",p);
974
975
	else
		sbbs->bputs(p);
976
	JS_RESUMEREQUEST(cx, rc);
977

978
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
979

980
	js_sprintf_free(p);
981
982
983
984
985

    return(JS_TRUE);
}

static JSBool
986
js_alert(JSContext *cx, uintN argc, jsval *arglist)
987
{
988
	jsval *argv=JS_ARGV(cx, arglist);
989
	sbbs_t*		sbbs;
990
	jsrefcount	rc;
deuce's avatar
deuce committed
991
	char		*cstr;
992

993
994
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

deuce's avatar
deuce committed
998
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
999
	if(cstr==NULL)
1000
1001
	    return(JS_FALSE);

1002
	rc=JS_SUSPENDREQUEST(cx);
1003
1004
1005
1006
1007
1008
1009
1010
	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
1011
	free(cstr);
1012
	JS_RESUMEREQUEST(cx, rc);
1013

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

1016
1017
1018
1019
    return(JS_TRUE);
}

static JSBool
1020
js_confirm(JSContext *cx, uintN argc, jsval *arglist)
1021
{
1022
	jsval *argv=JS_ARGV(cx, arglist);
1023
	sbbs_t*		sbbs;
1024
	jsrefcount	rc;
deuce's avatar
deuce committed
1025
	char		*cstr;
1026

1027
1028
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

deuce's avatar
deuce committed
1032
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
1033
	if(cstr==NULL)
1034
1035
	    return(JS_FALSE);

1036
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
1037
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->yesno(cstr)));
deuce's avatar
deuce committed