main.cpp 170 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
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
294
295
296
297
298
void call_socket_open_callback(BOOL open)
{
	if(startup!=NULL && startup->socket_open!=NULL) 
		startup->socket_open(startup->cbdata, open);
}

299
SOCKET open_socket(int type, const char* protocol)
300
301
302
303
304
{
	SOCKET	sock;
	char	error[256];

	sock=socket(AF_INET, type, IPPROTO_IP);
305
306
	if(sock!=INVALID_SOCKET)
		call_socket_open_callback(TRUE);
307
	if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
308
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
309
310
311
312

	return(sock);
}

313
// Used by sbbs_t::ftp_put() and js_accept()
deuce's avatar
deuce committed
314
SOCKET accept_socket(SOCKET s, union xp_sockaddr* addr, socklen_t* addrlen)
315
316
317
{
	SOCKET	sock;

deuce's avatar
deuce committed
318
	sock=accept(s,&addr->addr,addrlen);
319
320
	if(sock!=INVALID_SOCKET)
		call_socket_open_callback(TRUE);
321
322
323
324
325
326
327
328
329
330
331
332
333

	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);
334
	call_socket_open_callback(FALSE);
335
	if(result!=0 && ERROR_VALUE!=ENOTSOCK)
336
		lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
337
338
339
	return(result);
}

deuce's avatar
deuce committed
340
/* TODO: IPv6 */
341
342
343
344
345
346
u_long resolve_ip(char *addr)
{
	HOSTENT*	host;
	char*		p;

	if(*addr==0)
347
		return((u_long)INADDR_NONE);
348
349

	for(p=addr;*p;p++)
350
		if(*p!='.' && !isdigit((uchar)*p))
351
352
353
354
			break;
	if(!(*p))
		return(inet_addr(addr));
	if((host=gethostbyname(addr))==NULL) 
355
		return((u_long)INADDR_NONE);
356
357
358
359
360
	return(*((ulong*)host->h_addr_list[0]));
}

} /* extern "C" */

361
362
363
364
365
366
367
368
369
370
371
#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) {
372
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
373
374
375
376
		WSAInitialized=TRUE;
		return(TRUE);
	}

377
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
378
379
380
381
382
383
384
385
386
387
	return(FALSE);
}

#else /* No WINSOCK */

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

#endif

388
389
DLLEXPORT void DLLCALL sbbs_srand()
{
390
	DWORD seed;
391

392
	xp_randomize();
393
#if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
394
	int     rf,rd=0;
395

396
397
	if((rf=open(RANDOM_DEV, O_RDONLY|O_NONBLOCK))!=-1) {
		rd=read(rf, &seed, sizeof(seed));
398
399
		close(rf);
	}
deuce's avatar
deuce committed
400
	if (rd != sizeof(seed))
401
#endif
402
		seed = time32(NULL) ^ (uintmax_t)GetCurrentThreadId();
403
404

 	srand(seed);
405
406
407
	sbbs_random(10);	/* Throw away first number */
}

408
int DLLCALL sbbs_random(int n)
409
410
411
412
{
	return(xp_random(n));
}

413
414
#ifdef JAVASCRIPT

415
416
static js_server_props_t js_server_props;

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

432
433
434
435
	if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
		,NULL,NULL,flags))
		return(JS_FALSE);

436
437
438
439
440
441
442
443
444
445
446
	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;
	}

447
	return(JS_TRUE);
448
449
}

450
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
451
452

JSBool
453
DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
454
455
456
457
458
459
{
	JSString* js_str = JS_NewStringCopyZ(cx, str);

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

rswindell's avatar
rswindell committed
460
461
462
	if(ver < 10000)		/* auto convert 313 to 31300 */
		ver*=100;

463
	return(JS_DefineProperty(cx,obj,"_description"
464
465
466
			,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
		&& JS_DefineProperty(cx,obj,"_ver"
			,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
467
468
469
}

JSBool
470
DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
471
472
473
474
475
476
477
478
479
480
{
	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));
}

481
#ifdef BUILD_JSDOCS
482
483

static const char* method_array_name = "_method_list";
484
static const char* propver_array_name = "_property_ver_list";
485
486
487
488
489
490
491
492
493
494
495
496
497

/*
 * 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
498
499
	"null",
	"xml",
500
501
	"array",
	"alias",
deuce's avatar
deuce committed
502
	"undefined"
503
504
};

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

	return(JS_TRUE);
}

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

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

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

577
578
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
579
580
581
582

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

583
		method = JS_NewObject(cx, NULL, NULL, method_array);	/* exception here June-7-2003 */
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
613
614
615
616
617

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

618
		if(funcs[i].ver) {
619
			if((ver=funcs[i].ver) < 10000)		/* auto convert 313 to 31300 */
620
621
				ver*=100;
			val = INT_TO_JSVAL(ver);
622
623
624
			JS_SetProperty(cx,method, "ver", &val);
		}

625
626
627
628
629
630
631
632
		val=OBJECT_TO_JSVAL(method);
		if(!JS_SetElement(cx, method_array, len+i, &val))
			return(JS_FALSE);
	}

	return(JS_TRUE);
}

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

deuce's avatar
deuce committed
643
	if(props) {
644
645
		if(!js_DefineSyncProperties(cx, obj, props))
			ret=JS_FALSE;
deuce's avatar
deuce committed
646
	}
647
		
deuce's avatar
deuce committed
648
	if(funcs) {
deuce's avatar
deuce committed
649
		if(!js_DefineSyncMethods(cx, obj, funcs))
650
			ret=JS_FALSE;
deuce's avatar
deuce committed
651
	}
652

deuce's avatar
deuce committed
653
654
655
656
	if(consts) {
		if(!js_DefineConstIntegers(cx, obj, consts, flags))
			ret=JS_FALSE;
	}
657

658
659
660
	return(ret);
}

661
#else // NON-JSDOCS
662

663
664
665
666
667
668
669
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint i;

	for(i=0;props[i].name;i++) 
		if(!JS_DefinePropertyWithTinyId(cx, obj, 
670
			props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
671
672
673
674
675
676
			return(JS_FALSE);

	return(JS_TRUE);
}


677
JSBool 
deuce's avatar
deuce committed
678
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
679
{
680
	uint i;
681
682

	for(i=0;funcs[i].name;i++)
683
684
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
685
686
687
	return(JS_TRUE);
}

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

	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);
			}
		}
	}
715
	if(consts) {
deuce's avatar
deuce committed
716
		for(i=0;consts[i].name;i++) {
717
			if(name==NULL || strcmp(name, consts[i].name)==0) {
718
				val=INT_TO_JSVAL(consts[i].val);
719

deuce's avatar
deuce committed
720
				if(!JS_DefineProperty(cx, obj, consts[i].name, val ,NULL, NULL, flags))
721
722
723
724
725
726
727
					return(JS_FALSE);

				if(name)
					return(JS_TRUE);
			}
		}
	}
728
729
730
731

	return(JS_TRUE);
}

732
733
#endif

734
735
736
737
738
739
740
741
/* 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++) {
742
		val=INT_TO_JSVAL(ints[i].val);
743
744
745
746

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

748
749
750
	return(JS_TRUE);
}

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

763
764
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

765
766
767
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

768
769
770
771
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
772
773

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

793
	if(str==NULL)
794
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
795
	else
796
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
797
798
799
    return(JS_TRUE);
}

800
static JSBool
801
js_read(JSContext *cx, uintN argc, jsval *arglist)
802
{
803
	jsval *argv=JS_ARGV(cx, arglist);
804
805
806
	uchar*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
807
	jsrefcount	rc;
808

809
810
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

811
812
813
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

814
815
816
817
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
818
819
820
821

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

822
	rc=JS_SUSPENDREQUEST(cx);
823
	len=RingBufRead(&sbbs->inbuf,buf,len);
824
	JS_RESUMEREQUEST(cx, rc);
825
826

	if(len>0)
827
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len)));
828
829
830
831
832
833

	free(buf);
	return(JS_TRUE);
}

static JSBool
834
js_readln(JSContext *cx, uintN argc, jsval *arglist)
835
{
836
	jsval *argv=JS_ARGV(cx, arglist);
837
838
839
	char*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
840
	jsrefcount	rc;
841

842
843
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

847
848
849
850
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
851
852
853
854

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

855
	rc=JS_SUSPENDREQUEST(cx);
856
	len=sbbs->getstr(buf,len,K_NONE);
857
	JS_RESUMEREQUEST(cx, rc);
858
859

	if(len>0)
860
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf)));
861
862
863
864
865

	free(buf);
	return(JS_TRUE);
}

866
static JSBool
867
js_write(JSContext *cx, uintN argc, jsval *arglist)
868
{
869
	jsval *argv=JS_ARGV(cx, arglist);
870
    uintN		i;
871
    JSString*	str=NULL;
872
	sbbs_t*		sbbs;
873
	jsrefcount	rc;
874
875
	char		*cstr=NULL;
	size_t		cstr_sz=0;
876

877
878
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

879
880
881
882
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

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

	if(str==NULL)
900
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
901
	else
902
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
903
904
905
    return(JS_TRUE);
}

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

917
918
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

    return(JS_TRUE);
}

938
static JSBool
939
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
940
941
{
	sbbs_t*		sbbs;
942
	jsrefcount	rc;
943

944
945
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

949
	js_write(cx,argc,arglist);
950
	rc=JS_SUSPENDREQUEST(cx);
951
952
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);
953
	JS_RESUMEREQUEST(cx, rc);
954
955
956
957
958

    return(JS_TRUE);
}

static JSBool
959
js_printf(JSContext *cx, uintN argc, jsval *arglist)
960
{
961
	jsval *argv=JS_ARGV(cx, arglist);
962
963
	char*		p;
	sbbs_t*		sbbs;
964
	jsrefcount	rc;
965

966
967
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

968
969
970
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

971
972
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
973
974
975
		return(JS_FALSE);
	}

976
	rc=JS_SUSPENDREQUEST(cx);
977
	if(sbbs->online==ON_LOCAL)
978
		eprintf(LOG_INFO,"%s",p);
979
980
	else
		sbbs->bputs(p);
981
	JS_RESUMEREQUEST(cx, rc);
982

983
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
984

985
	js_sprintf_free(p);
986
987
988
989
990

    return(JS_TRUE);
}

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

998
999
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

deuce's avatar
deuce committed
1003
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
1004
	if(cstr==NULL)
1005
1006
	    return(JS_FALSE);

1007
	rc=JS_SUSPENDREQUEST(cx);
1008
1009
1010
1011
1012
1013
1014
1015
	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
1016
	free(cstr);
1017
	JS_RESUMEREQUEST(cx, rc);
1018

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

1021
1022
1023
1024
    return(JS_TRUE);
}

static JSBool
1025
js_confirm(JSContext *cx, uintN argc, jsval *arglist)
1026
{
1027
	jsval *argv=JS_ARGV(cx, arglist);
1028
	sbbs_t*		sbbs;
1029
	jsrefcount	rc;
deuce's avatar
deuce committed
1030
	char		*cstr;
1031

1032
1033
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

1034
1035
1036
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
1037
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
1038
	if(cstr==NULL)
1039
1040
	    return(JS_FALSE);

1041
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
1042
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->yesno(cstr)));
deuce's avatar
deuce committed
1043
	free(cstr);
1044
	JS_RESUMEREQUEST(cx, rc);
1045
1046
1047
	return(JS_TRUE);
}

1048
static JSBool
1049
js_deny(JSContext *cx, uintN argc, jsval *arglist)
1050
{
1051
	jsval *argv=JS_ARGV(cx, arglist);
1052
1053
	sbbs_t*		sbbs;
	jsrefcount	rc;
deuce's avatar
deuce committed
1054
	char		*cstr;
1055

1056
1057
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

1058
1059
1060
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
1061
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
1062
	if(cstr==NULL)
1063
1064
1065
	    return(JS_FALSE);

	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
1066
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->noyes(cstr)));
deuce's avatar
deuce committed
1067
	free(cstr);
1068
1069
1070
1071
1072
	JS_RESUMEREQUEST(cx, rc);
	return(JS_TRUE);
}


1073
static JSBool
1074
js_prompt(JSContext *cx, uintN argc, jsval *arglist)
1075
{
1076
	jsval *argv=JS_ARGV(cx, arglist);
1077
1078
1079
	char		instr[81];
    JSString *	str;
	sbbs_t*		sbbs;
1080
	jsrefcount	rc;
1081
    char*		prompt=NULL;
1082

1083
1084
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

1085
1086
1087
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

1088
1089
1090
1091
1092
	if(argc) {
		JSVALUE_TO_MSTRING(cx, argv[0], prompt, NULL);
		if(prompt==NULL)
			return(JS_FALSE);
	}
1093
1094

	if(argc>1) {
deuce's avatar
deuce committed
1095
		JSVALUE_TO_STRBUF(cx, argv[1], instr, sizeof(instr), NULL);
1096
1097
1098
	} else
		instr[0]=0;

1099
	rc=JS_SUSPENDREQUEST(cx);
1100
1101
1102
1103
	if(prompt != NULL) {
		sbbs->bprintf("\1n\1y\1h%s\1w: ",prompt);
		free(prompt);
	}
1104
1105

	if(!sbbs->getstr(instr,sizeof(instr)-1,K_EDIT)) {
1106
		JS_SET_RVAL(cx, arglist, JSVAL_NULL);
1107
		JS_RESUMEREQUEST(cx, rc);
1108
1109
		return(JS_TRUE);
	}
1110
	JS_RESUMEREQUEST(cx, rc);
1111
1112
1113
1114

	if((str=JS_NewStringCopyZ(cx, instr))==NULL)
	    return(JS_FALSE);

1115
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
1116
1117
1118
    return(JS_TRUE);
}

1119
static jsSyncMethodSpec js_global_functions[] = {
1120
	{"log",				js_log,				1,	JSTYPE_STRING,	JSDOCSTR("[level,] value [,value]")
1121
	,JSDOCSTR("add a line of text to the server and/or system log, "
1122
1123
		"<i>values</i> are typically string constants or variables, "
		"<i>level</i> is the debug level/priority (default: <tt>LOG_INFO</tt>)")
1124
	,311
1125
	},
1126
1127
	{"read",			js_read,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read up to count characters from input stream")
1128
	,311
1129
1130
1131
	},
	{"readln",			js_readln,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read a single line, up to count characters, from input stream")
1132
	,311
1133
	},
1134
	{"write",			js_write,			0,	JSTYPE_VOID,	JSDOCSTR("value [,value]")
1135
	,JSDOCSTR("send one or more values (typically strings) to the server output")
1136
	,311