main.cpp 175 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
 *																			*
 * 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"
39
#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
rswindell's avatar
rswindell committed
74
75
76
77
78
79
80
81
82
83
84
85

	static	protected_uint32_t	ssh_sessions;

	void ssh_session_destroy(SOCKET sock, CRYPT_SESSION session, int line)
	{
		int result = cryptDestroySession(session);

		if(result != 0)
			lprintf(LOG_ERR, "%04d SSH Error %d destroying Cryptlib Session %d from line %d"
				, sock, result, session, line);
		else {
			int32_t remain = protected_uint32_adjust(&ssh_sessions, -1);
86
			lprintf(LOG_DEBUG, "%04d SSH Cryptlib Session: %d destroyed from line %d (%d remain)"
rswindell's avatar
rswindell committed
87
88
89
90
91
92
93
94
				, sock, session, line, remain);
		}
	}

	#define SSH_END(sock) do {										\
		if(ssh) {													\
			pthread_mutex_lock(&sbbs->ssh_mutex);					\
			ssh_session_destroy(sock, sbbs->ssh_session, __LINE__);	\
95
			sbbs->ssh_mode = false;									\
rswindell's avatar
rswindell committed
96
97
			pthread_mutex_unlock(&sbbs->ssh_mutex);					\
		}															\
deuce's avatar
deuce committed
98
	} while(0)
99
#else
rswindell's avatar
rswindell committed
100
	#define	SSH_END(x)
101
102
#endif

103
104
volatile time_t	uptime=0;
volatile ulong	served=0;
105

106
static	protected_uint32_t node_threads_running;
107

108
109
110
111
112
113
114
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
115
struct xpms_set				*ts_set;
116
117
118
119
120
static	sbbs_t*	sbbs=NULL;
static	scfg_t	scfg;
static	char *	text[TOTAL_TEXT];
static	WORD	first_node;
static	WORD	last_node;
121
static	bool	terminate_server=false;
122
123
static	str_list_t recycle_semfiles;
static	str_list_t shutdown_semfiles;
124
125
static	link_list_t current_logins;
static	link_list_t current_connections;
126
127
128
#ifdef _THREAD_SUID_BROKEN
int	thread_suid_broken=TRUE;			/* NPTL is no longer broken */
#endif
129

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

deuce's avatar
deuce committed
140
#define GCESNN(status, sess, action) do {                              \
141
142
143
	char *GCES_estr;                                                    \
	int GCES_level;                                                      \
	get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level);\
deuce's avatar
deuce committed
144
	if (GCES_estr) {                                                       \
145
		lprintf(GCES_level, "SSH %s from %s", GCES_estr, __FUNCTION__);     \
deuce's avatar
deuce committed
146
		free_crypt_attrstr(GCES_estr);                                       \
147
	}                                                                         \
deuce's avatar
deuce committed
148
149
} while (0)

deuce's avatar
deuce committed
150
#define GCESS(status, sock, sess, action) do {                         \
151
152
153
	char *GCES_estr;                                                    \
	int GCES_level;                                                      \
	get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level);\
deuce's avatar
deuce committed
154
	if (GCES_estr) {                                                       \
155
		lprintf(GCES_level, "%04d SSH %s from %s", sock, GCES_estr, __FUNCTION__);                \
deuce's avatar
deuce committed
156
		free_crypt_attrstr(GCES_estr);                                       \
157
	}                                                                         \
deuce's avatar
deuce committed
158
159
} while (0)

160
161
162
163
164
#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) {                                                       \
165
		lprintf(GCES_level, "%s SSH %s from %s (session %d)", str, GCES_estr, __FUNCTION__, sess);                \
166
167
168
169
170
		free_crypt_attrstr(GCES_estr);                                       \
	}                                                                         \
} while (0)


171
172
173
174
extern "C" {

static bbs_startup_t* startup=NULL;

rswindell's avatar
rswindell committed
175
static const char* status(const char* str)
176
177
{
	if(startup!=NULL && startup->status!=NULL)
178
	    startup->status(startup->cbdata,str);
rswindell's avatar
rswindell committed
179
	return str;
180
181
182
183
184
}

static void update_clients()
{
	if(startup!=NULL && startup->clients!=NULL)
185
		startup->clients(startup->cbdata,protected_uint32_value(node_threads_running));
186
187
188
189
}

void client_on(SOCKET sock, client_t* client, BOOL update)
{
190
191
	if(!update)
		listAddNodeData(&current_connections, client->addr, strlen(client->addr)+1, sock, LAST_NODE);
192
	if(startup!=NULL && startup->client_on!=NULL)
193
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
194
195
196
197
}

static void client_off(SOCKET sock)
{
198
	listRemoveTaggedNode(&current_connections, sock, /* free_data */TRUE);
199
	if(startup!=NULL && startup->client_on!=NULL)
200
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
201
202
203
204
205
}

static void thread_up(BOOL setuid)
{
	if(startup!=NULL && startup->thread_up!=NULL)
206
		startup->thread_up(startup->cbdata,TRUE,setuid);
207
208
209
210
211
}

static void thread_down()
{
	if(startup!=NULL && startup->thread_up!=NULL)
212
		startup->thread_up(startup->cbdata,FALSE,FALSE);
213
214
}

215
int lputs(int level, const char* str)
216
{
217
	if(level <= LOG_ERR) {
218
219
220
		char errmsg[1024];
		SAFEPRINTF(errmsg, "term %s", str);
		errorlog(&scfg,startup==NULL ? NULL:startup->host_name, errmsg);
221
		if(startup!=NULL && startup->errormsg!=NULL)
222
			startup->errormsg(startup->cbdata,level,errmsg);
223
	}
224

225
	if(startup==NULL || startup->lputs==NULL || str==NULL || level > startup->log_level)
226
227
    	return(0);

228
229
230
231
232
#if defined(_WIN32)
	if(IsBadCodePtr((FARPROC)startup->lputs))
		return(0);
#endif

233
    return(startup->lputs(startup->cbdata,level,str));
234
235
}

236
237
int eputs(int level, const char *str)
{
238
239
240
	if(*str == 0)
		return 0;

241
242
243
244
245
246
247
248
249
250
251
252
	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));
}

253
int lprintf(int level, const char *fmt, ...)
254
255
256
257
258
259
260
261
{
	va_list argptr;
	char sbuf[1024];

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
262
    return(lputs(level,sbuf));
263
264
}

265
int eprintf(int level, const char *fmt, ...)
266
267
268
269
270
271
272
273
{
	va_list argptr;
	char sbuf[1024];

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

275
	strip_ctrl(sbuf, sbuf);
276

277
    return(eputs(level,truncsp(sbuf)));
278
}
279

280
/* Picks the right log callback function (event or term) based on the sbbs->cfg.node_num value */
281
/* Prepends the current node number and user alias (if applicable) */
282
283
int sbbs_t::lputs(int level, const char* str)
{
284
285
286
287
288
289
290
291
292
293
294
	char msg[2048];
	char prefix[32] = "";
	char user_str[64] = "";

	if(is_event_thread && event_code != NULL && *event_code)
		SAFEPRINTF(prefix, "%s ", event_code);
	else if(cfg.node_num && !is_event_thread)
		SAFEPRINTF(prefix, "Node %d ", cfg.node_num);
	else if(client_name[0])
		SAFEPRINTF(prefix, "%s ", client_name);
	if(useron.number)
295
		SAFEPRINTF(user_str, "<%s> ", useron.alias);
296
	SAFEPRINTF3(msg, "%s%s%s", prefix, user_str, str);
297
	strip_ctrl(msg, msg);
298
299
300
	if(is_event_thread)
		return ::eputs(level, msg);
	return ::lputs(level, msg);
301
302
}

303
304
305
306
307
308
309
310
311
312
313
314
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
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
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);
}

339
340
void call_socket_open_callback(BOOL open)
{
341
	if(startup!=NULL && startup->socket_open!=NULL)
342
343
344
		startup->socket_open(startup->cbdata, open);
}

345
SOCKET open_socket(int type, const char* protocol)
346
347
348
349
350
{
	SOCKET	sock;
	char	error[256];

	sock=socket(AF_INET, type, IPPROTO_IP);
351
352
	if(sock!=INVALID_SOCKET)
		call_socket_open_callback(TRUE);
353
	if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
354
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
355
356
357
358

	return(sock);
}

359
// Used by sbbs_t::ftp_put() and js_accept()
deuce's avatar
deuce committed
360
SOCKET accept_socket(SOCKET s, union xp_sockaddr* addr, socklen_t* addrlen)
361
362
363
{
	SOCKET	sock;

deuce's avatar
deuce committed
364
	sock=accept(s,&addr->addr,addrlen);
365
366
	if(sock!=INVALID_SOCKET)
		call_socket_open_callback(TRUE);
367
368
369
370
371
372
373
374
375
376
377
378
379

	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);
380
	call_socket_open_callback(FALSE);
381
	if(result!=0 && ERROR_VALUE!=ENOTSOCK)
382
		lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
383
384
385
	return(result);
}

deuce's avatar
deuce committed
386
/* TODO: IPv6 */
387
388
389
390
391
392
u_long resolve_ip(char *addr)
{
	HOSTENT*	host;
	char*		p;

	if(*addr==0)
393
		return((u_long)INADDR_NONE);
394
395

	for(p=addr;*p;p++)
396
		if(*p!='.' && !isdigit((uchar)*p))
397
398
399
			break;
	if(!(*p))
		return(inet_addr(addr));
400
	if((host=gethostbyname(addr))==NULL)
401
		return((u_long)INADDR_NONE);
402
403
404
405
406
	return(*((ulong*)host->h_addr_list[0]));
}

} /* extern "C" */

407
408
409
410
411
412
413
414
415
416
417
#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) {
418
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
419
420
421
422
		WSAInitialized=TRUE;
		return(TRUE);
	}

423
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
424
425
426
427
428
429
430
431
432
433
	return(FALSE);
}

#else /* No WINSOCK */

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

#endif

434
435
DLLEXPORT void DLLCALL sbbs_srand()
{
436
	DWORD seed;
437

438
	xp_randomize();
439
#if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
440
	int     rf,rd=0;
441

442
443
	if((rf=open(RANDOM_DEV, O_RDONLY|O_NONBLOCK))!=-1) {
		rd=read(rf, &seed, sizeof(seed));
444
445
		close(rf);
	}
deuce's avatar
deuce committed
446
	if (rd != sizeof(seed))
447
#endif
448
		seed = time32(NULL) ^ (uintmax_t)GetCurrentThreadId();
449
450

 	srand(seed);
451
452
453
	sbbs_random(10);	/* Throw away first number */
}

454
int DLLCALL sbbs_random(int n)
455
456
457
458
{
	return(xp_random(n));
}

459
460
#ifdef JAVASCRIPT

461
462
static js_server_props_t js_server_props;

463
JSBool
464
465
466
467
468
469
470
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;
471

472
473
474
	if(JS_GetProperty(cx,parent,name,&val) && val!=JSVAL_VOID)
		array=JSVAL_TO_OBJECT(val);
	else
475
476
		if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)	/* Assertion here, in _heap_alloc_dbg, June-21-2004 */
			return(JS_FALSE);								/* Caused by nntpservice.js? */
477

478
479
480
481
	if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
		,NULL,NULL,flags))
		return(JS_FALSE);

482
483
484
485
486
487
488
489
490
491
492
	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;
	}

493
	return(JS_TRUE);
494
495
}

496
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
497
498

JSBool
499
DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
500
501
502
503
504
505
{
	JSString* js_str = JS_NewStringCopyZ(cx, str);

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

rswindell's avatar
rswindell committed
506
507
508
	if(ver < 10000)		/* auto convert 313 to 31300 */
		ver*=100;

509
	return(JS_DefineProperty(cx,obj,"_description"
510
511
512
			,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
		&& JS_DefineProperty(cx,obj,"_ver"
			,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
513
514
515
}

JSBool
516
DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
517
518
519
520
521
522
523
524
525
526
{
	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));
}

527
#ifdef BUILD_JSDOCS
528
529

static const char* method_array_name = "_method_list";
530
static const char* propver_array_name = "_property_ver_list";
531
532
533
534
535
536
537
538
539
540
541
542
543

/*
 * 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
544
545
	"null",
	"xml",
546
547
	"array",
	"alias",
deuce's avatar
deuce committed
548
	"undefined"
549
550
};

551
552
553
554
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint		i;
555
	long		ver;
556
557
558
559
560
561
562
563
564
565
566
567
	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++) {
568
569
		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))
570
571
			return(JS_FALSE);
		if(props[i].flags&JSPROP_ENUMERATE) {	/* No need to version invisible props */
572
573
574
			if((ver=props[i].ver) < 10000)		/* auto convert 313 to 31300 */
				ver*=100;
			val = INT_TO_JSVAL(ver);
575
576
577
578
579
580
581
582
			if(!JS_SetElement(cx, array, len++, &val))
				return(JS_FALSE);
		}
	}

	return(JS_TRUE);
}

583
JSBool
deuce's avatar
deuce committed
584
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
585
586
587
{
	int			i;
	jsuint		len=0;
588
	long		ver;
589
590
591
592
	jsval		val;
	JSObject*	method;
	JSObject*	method_array;
	JSString*	js_str;
deuce's avatar
deuce committed
593
594
	size_t		str_len=0;
	char		*str=NULL;
595
596

	/* Return existing method_list array if it's already been created */
deuce's avatar
deuce committed
597
	if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID) {
598
		method_array=JSVAL_TO_OBJECT(val);
deuce's avatar
deuce committed
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
		// 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 {
614
		if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL)
615
			return(JS_FALSE);
deuce's avatar
deuce committed
616
617
		if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
				, NULL, NULL, 0))
618
			return(JS_FALSE);
deuce's avatar
deuce committed
619
	}
620
621
622

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

623
624
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
625
626
627
628

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

629
		method = JS_NewObject(cx, NULL, NULL, method_array);	/* exception here June-7-2003 */
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656

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

657
		if(funcs[i].desc!=NULL) {
658
659
660
661
662
663
			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);
		}

664
		if(funcs[i].ver) {
665
			if((ver=funcs[i].ver) < 10000)		/* auto convert 313 to 31300 */
666
667
				ver*=100;
			val = INT_TO_JSVAL(ver);
668
669
670
			JS_SetProperty(cx,method, "ver", &val);
		}

671
672
673
674
675
676
677
678
		val=OBJECT_TO_JSVAL(method);
		if(!JS_SetElement(cx, method_array, len+i, &val))
			return(JS_FALSE);
	}

	return(JS_TRUE);
}

679
680
681
682
683
/*
 * Always resolve all here since
 * 1) We'll always be enumerating anyways
 * 2) The speed penalty won't be seen in production code anyways
 */
684
685
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
686
687
688
{
	JSBool	ret=JS_TRUE;

deuce's avatar
deuce committed
689
	if(props) {
690
691
		if(!js_DefineSyncProperties(cx, obj, props))
			ret=JS_FALSE;
deuce's avatar
deuce committed
692
	}
693

deuce's avatar
deuce committed
694
	if(funcs) {
deuce's avatar
deuce committed
695
		if(!js_DefineSyncMethods(cx, obj, funcs))
696
			ret=JS_FALSE;
deuce's avatar
deuce committed
697
	}
698

deuce's avatar
deuce committed
699
700
701
702
	if(consts) {
		if(!js_DefineConstIntegers(cx, obj, consts, flags))
			ret=JS_FALSE;
	}
703

704
705
706
	return(ret);
}

707
#else // NON-JSDOCS
708

709
710
711
712
713
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint i;

714
715
	for(i=0;props[i].name;i++)
		if(!JS_DefinePropertyWithTinyId(cx, obj,
716
			props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
717
718
719
720
721
722
			return(JS_FALSE);

	return(JS_TRUE);
}


723
JSBool
deuce's avatar
deuce committed
724
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
725
{
726
	uint i;
727
728

	for(i=0;funcs[i].name;i++)
729
730
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
731
732
733
	return(JS_TRUE);
}

734
735
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
736
737
{
	uint i;
738
	jsval	val;
739
740
741
742

	if(props) {
		for(i=0;props[i].name;i++) {
			if(name==NULL || strcmp(name, props[i].name)==0) {
743
				if(!JS_DefinePropertyWithTinyId(cx, obj,
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
						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);
			}
		}
	}
761
	if(consts) {
deuce's avatar
deuce committed
762
		for(i=0;consts[i].name;i++) {
763
			if(name==NULL || strcmp(name, consts[i].name)==0) {
764
				val=INT_TO_JSVAL(consts[i].val);
765

deuce's avatar
deuce committed
766
				if(!JS_DefineProperty(cx, obj, consts[i].name, val ,NULL, NULL, flags))
767
768
769
770
771
772
773
					return(JS_FALSE);

				if(name)
					return(JS_TRUE);
			}
		}
	}
774
775
776
777

	return(JS_TRUE);
}

778
779
#endif

780
781
782
783
784
785
786
787
/* 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++) {
788
		val=INT_TO_JSVAL(ints[i].val);
789
790
791
792

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

794
795
796
	return(JS_TRUE);
}

797
static JSBool
798
js_log(JSContext *cx, uintN argc, jsval *arglist)
799
{
800
	jsval *argv=JS_ARGV(cx, arglist);
801
802
    uintN		i=0;
	int32		level=LOG_INFO;
803
    JSString*	str=NULL;
804
	sbbs_t*		sbbs;
805
	jsrefcount	rc;
deuce's avatar
deuce committed
806
807
	char		*line=NULL;
	size_t		line_sz=0;
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 > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
818
819

    for(; i<argc; i++) {
rswindell's avatar
rswindell committed
820
821
		if((str=JS_ValueToString(cx, argv[i]))==NULL) {
			FREE_AND_NULL(line);
deuce's avatar
deuce committed
822
			return(JS_FALSE);
rswindell's avatar
rswindell committed
823
		}
deuce's avatar
deuce committed
824
		JSSTRING_TO_RASTRING(cx, str, line, &line_sz, NULL);
deuce's avatar
deuce committed
825
		if(line==NULL)
826
		    return(JS_FALSE);
827
		rc=JS_SUSPENDREQUEST(cx);
828
		sbbs->lputs(level, line);
829
		JS_RESUMEREQUEST(cx, rc);
830
	}
rswindell's avatar
rswindell committed
831
832
	if(line != NULL)
		free(line);
833

834
	if(str==NULL)
835
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
836
	else
837
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
838
839
840
    return(JS_TRUE);
}

841
static JSBool
842
js_read(JSContext *cx, uintN argc, jsval *arglist)
843
{
844
	jsval *argv=JS_ARGV(cx, arglist);
845
846
847
	uchar*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
848
	jsrefcount	rc;
849

850
851
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

852
853
854
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

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

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

863
	rc=JS_SUSPENDREQUEST(cx);
864
	len=RingBufRead(&sbbs->inbuf,buf,len);
865
	JS_RESUMEREQUEST(cx, rc);
866
867

	if(len>0)
868
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len)));
869
870
871
872
873
874

	free(buf);
	return(JS_TRUE);
}

static JSBool
875
js_readln(JSContext *cx, uintN argc, jsval *arglist)
876
{
877
	jsval *argv=JS_ARGV(cx, arglist);
878
879
880
	char*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
881
	jsrefcount	rc;
882

883
884
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

888
889
890
891
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
892
893
894
895

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

896
	rc=JS_SUSPENDREQUEST(cx);
897
	len=sbbs->getstr(buf,len,K_NONE);
898
	JS_RESUMEREQUEST(cx, rc);
899
900

	if(len>0)
901
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf)));
902
903
904
905
906

	free(buf);
	return(JS_TRUE);
}

907
static JSBool
908
js_write(JSContext *cx, uintN argc, jsval *arglist)
909
{
910
	jsval *argv=JS_ARGV(cx, arglist);
911
    uintN		i;
912
    JSString*	str=NULL;
913
	sbbs_t*		sbbs;
914
	jsrefcount	rc;
915
916
	char		*cstr=NULL;
	size_t		cstr_sz=0;
917

918
919
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

    for (i = 0; i < argc; i++) {
924
925
		if((str=JS_ValueToString(cx, argv[i]))==NULL) {
			FREE_AND_NULL(cstr);
deuce's avatar
deuce committed
926
			return(JS_FALSE);
927
		}
deuce's avatar
deuce committed
928
		JSSTRING_TO_RASTRING(cx, str, cstr, &cstr_sz, NULL);
deuce's avatar
deuce committed
929
		if(cstr==NULL)
930
		    return(JS_FALSE);
931
		rc=JS_SUSPENDREQUEST(cx);
932
		if(!sbbs->online)
933
			sbbs->lputs(LOG_INFO, cstr);
934
		else
deuce's avatar
deuce committed
935
			sbbs->bputs(cstr);
936
		JS_RESUMEREQUEST(cx, rc);
937
	}
rswindell's avatar
rswindell committed
938
	FREE_AND_NULL(cstr);
939
940

	if(str==NULL)
941
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
942
	else
943
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
944
945
946
    return(JS_TRUE);
}

947
static JSBool
948
js_write_raw(JSContext *cx, uintN argc, jsval *arglist)
949
{
950
	jsval *argv=JS_ARGV(cx, arglist);
951
    uintN		i;
deuce's avatar
deuce committed
952
953
    char*		str=NULL;
    size_t		str_sz=0;
954
955
	size_t		len;
	sbbs_t*		sbbs;
956
	jsrefcount	rc;
957

958
959
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

960
961
962
963
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
964
		JSVALUE_TO_RASTRING(cx, argv[i], str, &str_sz, &len);
965
		if(str==NULL)
966
		    return(JS_FALSE);
967
968
		if(len < 1)
			continue;
969
		rc=JS_SUSPENDREQUEST(cx);
970
		sbbs->putcom(str, len);
971
		JS_RESUMEREQUEST(cx, rc);
972
	}
rswindell's avatar
rswindell committed
973
974
	if (str != NULL)
		free(str);
975
976
977
978

    return(JS_TRUE);
}

979
static JSBool
980
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
981
982
{
	sbbs_t*		sbbs;
983
	jsrefcount	rc;
984

985
986
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

987
988
989
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

990
	js_write(cx,argc,arglist);
991
	rc=JS_SUSPENDREQUEST(cx);
992
993
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);
994
	JS_RESUMEREQUEST(cx, rc);
995
996
997
998
999

    return(JS_TRUE);
}

static JSBool
1000
js_printf(JSContext *cx, uintN argc, jsval *arglist)
1001
{
1002
	jsval *argv=JS_ARGV(cx, arglist);
1003
1004
	char*		p;
	sbbs_t*		sbbs;
1005
	jsrefcount	rc;
1006

1007
1008
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

1009
1010
1011
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

1012
1013
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
1014
1015
1016
		return(JS_FALSE);
	}

1017
	rc=JS_SUSPENDREQUEST(cx);
1018
	if(!sbbs->online)
1019
		sbbs->lputs(LOG_INFO, p);
1020
1021
	else
		sbbs->bputs(p);
1022
	JS_RESUMEREQUEST(cx, rc);
1023

1024
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
1025

1026
	js_sprintf_free(p);
1027
1028
1029
1030
1031

    return(JS_TRUE);
}

static JSBool
1032
js_alert(JSContext *cx, uintN argc, jsval *arglist)
1033
{
1034
	jsval *argv=JS_ARGV(cx, arglist);
1035
	sbbs_t*		sbbs;
1036
	jsrefcount	rc;
deuce's avatar
deuce committed
1037
	char		*cstr;
1038

1039
1040
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

1041
1042
1043
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
1044
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
1045
	if(cstr==NULL)
1046
1047
	    return(JS_FALSE);

1048
	rc=JS_SUSPENDREQUEST(cx);
1049
	if(!sbbs->online)
1050
		lputs(LOG_WARNING, cstr);
1051
1052
1053
1054
1055
1056
	else {
		sbbs->attr(sbbs->cfg.color[clr_err]);
		sbbs->bputs(cstr);
		sbbs->attr(LIGHTGRAY);
		sbbs->bputs(crlf);
	}
deuce's avatar
deuce committed
1057
	free(cstr);
1058
	JS_RESUMEREQUEST(cx, rc);
1059

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

1062
1063
1064
1065
    return(JS_TRUE);
}

static JSBool
1066
js_confirm(JSContext *cx, uintN argc, jsval *arglist)
1067
{
1068
	jsval *argv=JS_ARGV(cx, arglist);
1069
	sbbs_t*		sbbs;
1070
	jsrefcount	rc;
deuce's avatar
deuce committed
1071
	char		*cstr;
1072

1073
1074
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

1075
1076
1077
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
1078
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
1079
	if(cstr==NULL)
1080
1081
	    return(JS_FALSE);

1082
	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
1083
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->yesno(cstr)));
deuce's avatar
deuce committed
1084
	free(cstr);
1085
	JS_RESUMEREQUEST(cx, rc);
1086
1087
1088
	return(JS_TRUE);
}

1089
static JSBool
1090
js_deny(JSContext *cx, uintN argc, jsval *arglist)
1091
{
1092
	jsval *argv=JS_ARGV(cx, arglist);
1093
1094
	sbbs_t*		sbbs;
	jsrefcount	rc;
deuce's avatar
deuce committed
1095
	char		*cstr;
1096

1097
1098
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

1099
1100
1101
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

deuce's avatar
deuce committed
1102
	JSVALUE_TO_MSTRING(cx, argv[0], cstr, NULL);
deuce's avatar
deuce committed
1103
	if(cstr==NULL)
1104
1105
1106
	    return(JS_FALSE);

	rc=JS_SUSPENDREQUEST(cx);
deuce's avatar
deuce committed
1107
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->noyes(cstr)));
deuce's avatar
deuce committed
1108
	free(cstr);
1109
1110
1111
1112
1113
	JS_RESUMEREQUEST(cx, rc);
	return(JS_TRUE);
}


1114
static JSBool
1115
js_prompt(JSContext *cx, uintN argc, jsval *arglist)
1116
{
1117
	jsval *argv=JS_ARGV(cx, arglist);
1118
1119
1120
	char		instr[81];
    JSString *	str;
	sbbs_t*		sbbs;
1121
	jsrefcount	rc;
1122
    char*		prompt=NULL;
1123