main.cpp 171 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
#define GCES(status, node, sess, action) do {                          \
113
114
115
	char *GCES_estr;                                                    \
	int GCES_level;                                                      \
	get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level);\
deuce's avatar
deuce committed
116
	if (GCES_estr) {                                                       \
117
		lprintf(GCES_level, "Node %d SSH %s from %s", node, GCES_estr, __FUNCTION__);             \
deuce's avatar
deuce committed
118
		free_crypt_attrstr(GCES_estr);                                       \
119
	}                                                                         \
deuce's avatar
deuce committed
120
121
} while (0)

deuce's avatar
deuce committed
122
#define GCESNN(status, sess, action) do {                              \
123
124
125
	char *GCES_estr;                                                    \
	int GCES_level;                                                      \
	get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level);\
deuce's avatar
deuce committed
126
	if (GCES_estr) {                                                       \
127
		lprintf(GCES_level, "SSH %s from %s", GCES_estr, __FUNCTION__);     \
deuce's avatar
deuce committed
128
		free_crypt_attrstr(GCES_estr);                                       \
129
	}                                                                         \
deuce's avatar
deuce committed
130
131
} while (0)

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

142
143
144
145
146
147
148
149
150
151
152
#define GCESSTR(status, str, sess, action) do {                         \
	char *GCES_estr;                                                    \
	int GCES_level;                                                      \
	get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level);\
	if (GCES_estr) {                                                       \
		lprintf(GCES_level, "%s SSH %s from %s", str, GCES_estr, __FUNCTION__);                \
		free_crypt_attrstr(GCES_estr);                                       \
	}                                                                         \
} while (0)


153
154
155
156
extern "C" {

static bbs_startup_t* startup=NULL;

rswindell's avatar
rswindell committed
157
static const char* status(const char* str)
158
159
{
	if(startup!=NULL && startup->status!=NULL)
160
	    startup->status(startup->cbdata,str);
rswindell's avatar
rswindell committed
161
	return str;
162
163
164
165
166
}

static void update_clients()
{
	if(startup!=NULL && startup->clients!=NULL)
167
		startup->clients(startup->cbdata,protected_uint32_value(node_threads_running));
168
169
170
171
}

void client_on(SOCKET sock, client_t* client, BOOL update)
{
172
173
	if(!update)
		listAddNodeData(&current_connections, client->addr, strlen(client->addr)+1, sock, LAST_NODE);
174
	if(startup!=NULL && startup->client_on!=NULL)
175
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
176
177
178
179
}

static void client_off(SOCKET sock)
{
180
	listRemoveTaggedNode(&current_connections, sock, /* free_data */TRUE);
181
	if(startup!=NULL && startup->client_on!=NULL)
182
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
183
184
185
186
187
}

static void thread_up(BOOL setuid)
{
	if(startup!=NULL && startup->thread_up!=NULL)
188
		startup->thread_up(startup->cbdata,TRUE,setuid);
189
190
191
192
193
}

static void thread_down()
{
	if(startup!=NULL && startup->thread_up!=NULL)
194
		startup->thread_up(startup->cbdata,FALSE,FALSE);
195
196
}

197
int lputs(int level, const char* str)
198
{
199
	if(level <= LOG_ERR) {
200
		errorlog(&scfg,startup==NULL ? NULL:startup->host_name, str);
201
202
203
		if(startup!=NULL && startup->errormsg!=NULL)
			startup->errormsg(startup->cbdata,level,str);
	}
204

205
	if(startup==NULL || startup->lputs==NULL || str==NULL || level > startup->log_level)
206
207
    	return(0);

208
209
210
211
212
#if defined(_WIN32)
	if(IsBadCodePtr((FARPROC)startup->lputs))
		return(0);
#endif

213
    return(startup->lputs(startup->cbdata,level,str));
214
215
}

216
217
int eputs(int level, const char *str)
{
218
219
220
	if(*str == 0)
		return 0;

221
222
223
224
225
226
227
228
229
230
231
232
	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));
}

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

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

245
int eprintf(int level, const char *fmt, ...)
246
247
248
249
250
251
252
253
{
	va_list argptr;
	char sbuf[1024];

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

255
	strip_ctrl(sbuf, sbuf);
256

257
    return(eputs(level,truncsp(sbuf)));
258
}
259

260
261
262
263
264
265
/* 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);
266
267
}

268
269
270
271
272
273
274
275
276
277
278
279
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
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
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);
}

304
305
306
307
308
309
void call_socket_open_callback(BOOL open)
{
	if(startup!=NULL && startup->socket_open!=NULL) 
		startup->socket_open(startup->cbdata, open);
}

310
SOCKET open_socket(int type, const char* protocol)
311
312
313
314
315
{
	SOCKET	sock;
	char	error[256];

	sock=socket(AF_INET, type, IPPROTO_IP);
316
317
	if(sock!=INVALID_SOCKET)
		call_socket_open_callback(TRUE);
318
	if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
319
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
320
321
322
323

	return(sock);
}

324
// Used by sbbs_t::ftp_put() and js_accept()
deuce's avatar
deuce committed
325
SOCKET accept_socket(SOCKET s, union xp_sockaddr* addr, socklen_t* addrlen)
326
327
328
{
	SOCKET	sock;

deuce's avatar
deuce committed
329
	sock=accept(s,&addr->addr,addrlen);
330
331
	if(sock!=INVALID_SOCKET)
		call_socket_open_callback(TRUE);
332
333
334
335
336
337
338
339
340
341
342
343
344

	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);
345
	call_socket_open_callback(FALSE);
346
	if(result!=0 && ERROR_VALUE!=ENOTSOCK)
347
		lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
348
349
350
	return(result);
}

deuce's avatar
deuce committed
351
/* TODO: IPv6 */
352
353
354
355
356
357
u_long resolve_ip(char *addr)
{
	HOSTENT*	host;
	char*		p;

	if(*addr==0)
358
		return((u_long)INADDR_NONE);
359
360

	for(p=addr;*p;p++)
361
		if(*p!='.' && !isdigit((uchar)*p))
362
363
364
365
			break;
	if(!(*p))
		return(inet_addr(addr));
	if((host=gethostbyname(addr))==NULL) 
366
		return((u_long)INADDR_NONE);
367
368
369
370
371
	return(*((ulong*)host->h_addr_list[0]));
}

} /* extern "C" */

372
373
374
375
376
377
378
379
380
381
382
#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) {
383
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
384
385
386
387
		WSAInitialized=TRUE;
		return(TRUE);
	}

388
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
389
390
391
392
393
394
395
396
397
398
	return(FALSE);
}

#else /* No WINSOCK */

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

#endif

399
400
DLLEXPORT void DLLCALL sbbs_srand()
{
401
	DWORD seed;
402

403
	xp_randomize();
404
#if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
405
	int     rf,rd=0;
406

407
408
	if((rf=open(RANDOM_DEV, O_RDONLY|O_NONBLOCK))!=-1) {
		rd=read(rf, &seed, sizeof(seed));
409
410
		close(rf);
	}
deuce's avatar
deuce committed
411
	if (rd != sizeof(seed))
412
#endif
413
		seed = time32(NULL) ^ (uintmax_t)GetCurrentThreadId();
414
415

 	srand(seed);
416
417
418
	sbbs_random(10);	/* Throw away first number */
}

419
int DLLCALL sbbs_random(int n)
420
421
422
423
{
	return(xp_random(n));
}

424
425
#ifdef JAVASCRIPT

426
427
static js_server_props_t js_server_props;

428
429
430
431
432
433
434
435
436
437
438
439
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
440
441
		if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)	/* Assertion here, in _heap_alloc_dbg, June-21-2004 */
			return(JS_FALSE);								/* Caused by nntpservice.js? */
442

443
444
445
446
	if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
		,NULL,NULL,flags))
		return(JS_FALSE);

447
448
449
450
451
452
453
454
455
456
457
	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;
	}

458
	return(JS_TRUE);
459
460
}

461
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
462
463

JSBool
464
DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
465
466
467
468
469
470
{
	JSString* js_str = JS_NewStringCopyZ(cx, str);

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

rswindell's avatar
rswindell committed
471
472
473
	if(ver < 10000)		/* auto convert 313 to 31300 */
		ver*=100;

474
	return(JS_DefineProperty(cx,obj,"_description"
475
476
477
			,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
		&& JS_DefineProperty(cx,obj,"_ver"
			,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
478
479
480
}

JSBool
481
DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
482
483
484
485
486
487
488
489
490
491
{
	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));
}

492
#ifdef BUILD_JSDOCS
493
494

static const char* method_array_name = "_method_list";
495
static const char* propver_array_name = "_property_ver_list";
496
497
498
499
500
501
502
503
504
505
506
507
508

/*
 * 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
509
510
	"null",
	"xml",
511
512
	"array",
	"alias",
deuce's avatar
deuce committed
513
	"undefined"
514
515
};

516
517
518
519
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint		i;
520
	long		ver;
521
522
523
524
525
526
527
528
529
530
531
532
	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++) {
533
534
		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))
535
536
			return(JS_FALSE);
		if(props[i].flags&JSPROP_ENUMERATE) {	/* No need to version invisible props */
537
538
539
			if((ver=props[i].ver) < 10000)		/* auto convert 313 to 31300 */
				ver*=100;
			val = INT_TO_JSVAL(ver);
540
541
542
543
544
545
546
547
			if(!JS_SetElement(cx, array, len++, &val))
				return(JS_FALSE);
		}
	}

	return(JS_TRUE);
}

548
JSBool 
deuce's avatar
deuce committed
549
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
550
551
552
{
	int			i;
	jsuint		len=0;
553
	long		ver;
554
555
556
557
	jsval		val;
	JSObject*	method;
	JSObject*	method_array;
	JSString*	js_str;
deuce's avatar
deuce committed
558
559
	size_t		str_len=0;
	char		*str=NULL;
560
561

	/* Return existing method_list array if it's already been created */
deuce's avatar
deuce committed
562
	if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID) {
563
		method_array=JSVAL_TO_OBJECT(val);
deuce's avatar
deuce committed
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
		// 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 {
579
580
		if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL) 
			return(JS_FALSE);
deuce's avatar
deuce committed
581
582
		if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
				, NULL, NULL, 0))
583
			return(JS_FALSE);
deuce's avatar
deuce committed
584
	}
585
586
587

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

588
589
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
590
591
592
593

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

594
		method = JS_NewObject(cx, NULL, NULL, method_array);	/* exception here June-7-2003 */
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628

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

629
		if(funcs[i].ver) {
630
			if((ver=funcs[i].ver) < 10000)		/* auto convert 313 to 31300 */
631
632
				ver*=100;
			val = INT_TO_JSVAL(ver);
633
634
635
			JS_SetProperty(cx,method, "ver", &val);
		}

636
637
638
639
640
641
642
643
		val=OBJECT_TO_JSVAL(method);
		if(!JS_SetElement(cx, method_array, len+i, &val))
			return(JS_FALSE);
	}

	return(JS_TRUE);
}

644
645
646
647
648
/*
 * Always resolve all here since
 * 1) We'll always be enumerating anyways
 * 2) The speed penalty won't be seen in production code anyways
 */
649
650
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
651
652
653
{
	JSBool	ret=JS_TRUE;

deuce's avatar
deuce committed
654
	if(props) {
655
656
		if(!js_DefineSyncProperties(cx, obj, props))
			ret=JS_FALSE;
deuce's avatar
deuce committed
657
	}
658
		
deuce's avatar
deuce committed
659
	if(funcs) {
deuce's avatar
deuce committed
660
		if(!js_DefineSyncMethods(cx, obj, funcs))
661
			ret=JS_FALSE;
deuce's avatar
deuce committed
662
	}
663

deuce's avatar
deuce committed
664
665
666
667
	if(consts) {
		if(!js_DefineConstIntegers(cx, obj, consts, flags))
			ret=JS_FALSE;
	}
668

669
670
671
	return(ret);
}

672
#else // NON-JSDOCS
673

674
675
676
677
678
679
680
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint i;

	for(i=0;props[i].name;i++) 
		if(!JS_DefinePropertyWithTinyId(cx, obj, 
681
			props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
682
683
684
685
686
687
			return(JS_FALSE);

	return(JS_TRUE);
}


688
JSBool 
deuce's avatar
deuce committed
689
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
690
{
691
	uint i;
692
693

	for(i=0;funcs[i].name;i++)
694
695
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
696
697
698
	return(JS_TRUE);
}

699
700
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
701
702
{
	uint i;
703
	jsval	val;
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725

	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);
			}
		}
	}
726
	if(consts) {
deuce's avatar
deuce committed
727
		for(i=0;consts[i].name;i++) {
728
			if(name==NULL || strcmp(name, consts[i].name)==0) {
729
				val=INT_TO_JSVAL(consts[i].val);
730

deuce's avatar
deuce committed
731
				if(!JS_DefineProperty(cx, obj, consts[i].name, val ,NULL, NULL, flags))
732
733
734
735
736
737
738
					return(JS_FALSE);

				if(name)
					return(JS_TRUE);
			}
		}
	}
739
740
741
742

	return(JS_TRUE);
}

743
744
#endif

745
746
747
748
749
750
751
752
/* 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++) {
753
		val=INT_TO_JSVAL(ints[i].val);
754
755
756
757

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

759
760
761
	return(JS_TRUE);
}

762
static JSBool
763
js_log(JSContext *cx, uintN argc, jsval *arglist)
764
{
765
	jsval *argv=JS_ARGV(cx, arglist);
766
767
    uintN		i=0;
	int32		level=LOG_INFO;
768
    JSString*	str=NULL;
769
	sbbs_t*		sbbs;
770
	jsrefcount	rc;
deuce's avatar
deuce committed
771
772
	char		*line=NULL;
	size_t		line_sz=0;
773

774
775
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

776
777
778
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

779
780
781
782
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
783
784

    for(; i<argc; i++) {
rswindell's avatar
rswindell committed
785
786
		if((str=JS_ValueToString(cx, argv[i]))==NULL) {
			FREE_AND_NULL(line);
deuce's avatar
deuce committed
787
			return(JS_FALSE);
rswindell's avatar
rswindell committed
788
		}
deuce's avatar
deuce committed
789
		JSSTRING_TO_RASTRING(cx, str, line, &line_sz, NULL);
deuce's avatar
deuce committed
790
		if(line==NULL)
791
		    return(JS_FALSE);
792
		rc=JS_SUSPENDREQUEST(cx);
793
		if(sbbs->online==ON_LOCAL) {
deuce's avatar
deuce committed
794
795
796
			if(startup!=NULL && startup->event_lputs!=NULL && level <= startup->log_level) {
				startup->event_lputs(startup->event_cbdata,level,line);
			}
797
		} else
deuce's avatar
deuce committed
798
			lprintf(level,"Node %d %s", sbbs->cfg.node_num, line);
799
		JS_RESUMEREQUEST(cx, rc);
800
	}
rswindell's avatar
rswindell committed
801
802
	if(line != NULL)
		free(line);
803

804
	if(str==NULL)
805
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
806
	else
807
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
808
809
810
    return(JS_TRUE);
}

811
static JSBool
812
js_read(JSContext *cx, uintN argc, jsval *arglist)
813
{
814
	jsval *argv=JS_ARGV(cx, arglist);
815
816
817
	uchar*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
818
	jsrefcount	rc;
819

820
821
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

822
823
824
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

825
826
827
828
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
829
830
831
832

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

833
	rc=JS_SUSPENDREQUEST(cx);
834
	len=RingBufRead(&sbbs->inbuf,buf,len);
835
	JS_RESUMEREQUEST(cx, rc);
836
837

	if(len>0)
838
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len)));
839
840
841
842
843
844

	free(buf);
	return(JS_TRUE);
}

static JSBool
845
js_readln(JSContext *cx, uintN argc, jsval *arglist)
846
{
847
	jsval *argv=JS_ARGV(cx, arglist);
848
849
850
	char*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
851
	jsrefcount	rc;
852

853
854
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

855
856
857
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

858
859
860
861
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
862
863
864
865

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

866
	rc=JS_SUSPENDREQUEST(cx);
867
	len=sbbs->getstr(buf,len,K_NONE);
868
	JS_RESUMEREQUEST(cx, rc);
869
870

	if(len>0)
871
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf)));
872
873
874
875
876

	free(buf);
	return(JS_TRUE);
}

877
static JSBool
878
js_write(JSContext *cx, uintN argc, jsval *arglist)
879
{
880
	jsval *argv=JS_ARGV(cx, arglist);
881
    uintN		i;
882
    JSString*	str=NULL;
883
	sbbs_t*		sbbs;
884
	jsrefcount	rc;
885
886
	char		*cstr=NULL;
	size_t		cstr_sz=0;
887

888
889
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

    for (i = 0; i < argc; i++) {
894
895
		if((str=JS_ValueToString(cx, argv[i]))==NULL) {
			FREE_AND_NULL(cstr);
deuce's avatar
deuce committed
896
			return(JS_FALSE);
897
		}
deuce's avatar
deuce committed
898
		JSSTRING_TO_RASTRING(cx, str, cstr, &cstr_sz, NULL);
deuce's avatar
deuce committed
899
		if(cstr==NULL)
900
		    return(JS_FALSE);
901
		rc=JS_SUSPENDREQUEST(cx);
902
		if(sbbs->online==ON_LOCAL)
deuce's avatar
deuce committed
903
			eprintf(LOG_INFO,"%s",cstr);
904
		else
deuce's avatar
deuce committed
905
			sbbs->bputs(cstr);
906
		JS_RESUMEREQUEST(cx, rc);
907
	}
rswindell's avatar
rswindell committed
908
	FREE_AND_NULL(cstr);
909
910

	if(str==NULL)
911
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
912
	else
913
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
914
915
916
    return(JS_TRUE);
}

917
static JSBool
918
js_write_raw(JSContext *cx, uintN argc, jsval *arglist)
919
{
920
	jsval *argv=JS_ARGV(cx, arglist);
921
    uintN		i;
deuce's avatar
deuce committed
922
923
    char*		str=NULL;
    size_t		str_sz=0;
924
925
	size_t		len;
	sbbs_t*		sbbs;
926
	jsrefcount	rc;
927

928
929
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

930
931
932
933
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
934
		JSVALUE_TO_RASTRING(cx, argv[i], str, &str_sz, &len);
935
		if(str==NULL)
936
		    return(JS_FALSE);
937
938
		if(len < 1)
			continue;
939
		rc=JS_SUSPENDREQUEST(cx);
940
		sbbs->putcom(str, len);
941
		JS_RESUMEREQUEST(cx, rc);
942
	}
rswindell's avatar
rswindell committed
943
944
	if (str != NULL)
		free(str);
945
946
947
948

    return(JS_TRUE);
}

949
static JSBool
950
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
951
952
{
	sbbs_t*		sbbs;
953
	jsrefcount	rc;
954

955
956
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

957
958
959
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

960
	js_write(cx,argc,arglist);
961
	rc=JS_SUSPENDREQUEST(cx);
962
963
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);
964
	JS_RESUMEREQUEST(cx, rc);
965
966
967
968
969

    return(JS_TRUE);
}

static JSBool
970
js_printf(JSContext *cx, uintN argc, jsval *arglist)
971
{
972
	jsval *argv=JS_ARGV(cx, arglist);
973
974
	char*		p;
	sbbs_t*		sbbs;
975
	jsrefcount	rc;
976

977
978
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

982
983
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
984
985
986
		return(JS_FALSE);
	}

987
	rc=JS_SUSPENDREQUEST(cx);
988
	if(sbbs->online==ON_LOCAL)
989
		eprintf(LOG_INFO,"%s",p);
990
991
	else
		sbbs->bputs(p);
992
	JS_RESUMEREQUEST(cx, rc);
993

994
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
995

996
	js_sprintf_free(p);
997
998
999
1000
1001

    return(JS_TRUE);
}

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

1009
1010
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

1011
1012
1013
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

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

1018
	rc=JS_SUSPENDREQUEST(cx);
1019
1020
1021
1022
1023
1024
1025
1026
	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
1027
	free(cstr);
1028
	JS_RESUMEREQUEST(cx, rc);
1029

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

1032
1033
1034
1035
    return(JS_TRUE);
}

static JSBool
1036
js_confirm(JSContext *cx, uintN argc, jsval *arglist)
1037
{
1038
	jsval *argv=JS_ARGV(cx, arglist);
1039
	sbbs_t*		sbbs;