main.cpp 172 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
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
		errorlog(&scfg,startup==NULL ? NULL:startup->host_name, str);
219
220
221
		if(startup!=NULL && startup->errormsg!=NULL)
			startup->errormsg(startup->cbdata,level,str);
	}
222

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

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

231
    return(startup->lputs(startup->cbdata,level,str));
232
233
}

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

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

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

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

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

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

273
	strip_ctrl(sbuf, sbuf);
274

275
    return(eputs(level,truncsp(sbuf)));
276
}
277

278
279
280
281
282
283
/* 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);
284
285
}

286
287
288
289
290
291
292
293
294
295
296
297
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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
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);
}

322
323
324
325
326
327
void call_socket_open_callback(BOOL open)
{
	if(startup!=NULL && startup->socket_open!=NULL) 
		startup->socket_open(startup->cbdata, open);
}

328
SOCKET open_socket(int type, const char* protocol)
329
330
331
332
333
{
	SOCKET	sock;
	char	error[256];

	sock=socket(AF_INET, type, IPPROTO_IP);
334
335
	if(sock!=INVALID_SOCKET)
		call_socket_open_callback(TRUE);
336
	if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
337
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
338
339
340
341

	return(sock);
}

342
// Used by sbbs_t::ftp_put() and js_accept()
deuce's avatar
deuce committed
343
SOCKET accept_socket(SOCKET s, union xp_sockaddr* addr, socklen_t* addrlen)
344
345
346
{
	SOCKET	sock;

deuce's avatar
deuce committed
347
	sock=accept(s,&addr->addr,addrlen);
348
349
	if(sock!=INVALID_SOCKET)
		call_socket_open_callback(TRUE);
350
351
352
353
354
355
356
357
358
359
360
361
362

	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);
363
	call_socket_open_callback(FALSE);
364
	if(result!=0 && ERROR_VALUE!=ENOTSOCK)
365
		lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
366
367
368
	return(result);
}

deuce's avatar
deuce committed
369
/* TODO: IPv6 */
370
371
372
373
374
375
u_long resolve_ip(char *addr)
{
	HOSTENT*	host;
	char*		p;

	if(*addr==0)
376
		return((u_long)INADDR_NONE);
377
378

	for(p=addr;*p;p++)
379
		if(*p!='.' && !isdigit((uchar)*p))
380
381
382
383
			break;
	if(!(*p))
		return(inet_addr(addr));
	if((host=gethostbyname(addr))==NULL) 
384
		return((u_long)INADDR_NONE);
385
386
387
388
389
	return(*((ulong*)host->h_addr_list[0]));
}

} /* extern "C" */

390
391
392
393
394
395
396
397
398
399
400
#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) {
401
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
402
403
404
405
		WSAInitialized=TRUE;
		return(TRUE);
	}

406
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
407
408
409
410
411
412
413
414
415
416
	return(FALSE);
}

#else /* No WINSOCK */

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

#endif

417
418
DLLEXPORT void DLLCALL sbbs_srand()
{
419
	DWORD seed;
420

421
	xp_randomize();
422
#if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
423
	int     rf,rd=0;
424

425
426
	if((rf=open(RANDOM_DEV, O_RDONLY|O_NONBLOCK))!=-1) {
		rd=read(rf, &seed, sizeof(seed));
427
428
		close(rf);
	}
deuce's avatar
deuce committed
429
	if (rd != sizeof(seed))
430
#endif
431
		seed = time32(NULL) ^ (uintmax_t)GetCurrentThreadId();
432
433

 	srand(seed);
434
435
436
	sbbs_random(10);	/* Throw away first number */
}

437
int DLLCALL sbbs_random(int n)
438
439
440
441
{
	return(xp_random(n));
}

442
443
#ifdef JAVASCRIPT

444
445
static js_server_props_t js_server_props;

446
447
448
449
450
451
452
453
454
455
456
457
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
458
459
		if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)	/* Assertion here, in _heap_alloc_dbg, June-21-2004 */
			return(JS_FALSE);								/* Caused by nntpservice.js? */
460

461
462
463
464
	if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
		,NULL,NULL,flags))
		return(JS_FALSE);

465
466
467
468
469
470
471
472
473
474
475
	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;
	}

476
	return(JS_TRUE);
477
478
}

479
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
480
481

JSBool
482
DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
483
484
485
486
487
488
{
	JSString* js_str = JS_NewStringCopyZ(cx, str);

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

rswindell's avatar
rswindell committed
489
490
491
	if(ver < 10000)		/* auto convert 313 to 31300 */
		ver*=100;

492
	return(JS_DefineProperty(cx,obj,"_description"
493
494
495
			,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
		&& JS_DefineProperty(cx,obj,"_ver"
			,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
496
497
498
}

JSBool
499
DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
500
501
502
503
504
505
506
507
508
509
{
	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));
}

510
#ifdef BUILD_JSDOCS
511
512

static const char* method_array_name = "_method_list";
513
static const char* propver_array_name = "_property_ver_list";
514
515
516
517
518
519
520
521
522
523
524
525
526

/*
 * 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
527
528
	"null",
	"xml",
529
530
	"array",
	"alias",
deuce's avatar
deuce committed
531
	"undefined"
532
533
};

534
535
536
537
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint		i;
538
	long		ver;
539
540
541
542
543
544
545
546
547
548
549
550
	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++) {
551
552
		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))
553
554
			return(JS_FALSE);
		if(props[i].flags&JSPROP_ENUMERATE) {	/* No need to version invisible props */
555
556
557
			if((ver=props[i].ver) < 10000)		/* auto convert 313 to 31300 */
				ver*=100;
			val = INT_TO_JSVAL(ver);
558
559
560
561
562
563
564
565
			if(!JS_SetElement(cx, array, len++, &val))
				return(JS_FALSE);
		}
	}

	return(JS_TRUE);
}

566
JSBool 
deuce's avatar
deuce committed
567
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
568
569
570
{
	int			i;
	jsuint		len=0;
571
	long		ver;
572
573
574
575
	jsval		val;
	JSObject*	method;
	JSObject*	method_array;
	JSString*	js_str;
deuce's avatar
deuce committed
576
577
	size_t		str_len=0;
	char		*str=NULL;
578
579

	/* Return existing method_list array if it's already been created */
deuce's avatar
deuce committed
580
	if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID) {
581
		method_array=JSVAL_TO_OBJECT(val);
deuce's avatar
deuce committed
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
		// 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 {
597
598
		if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL) 
			return(JS_FALSE);
deuce's avatar
deuce committed
599
600
		if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
				, NULL, NULL, 0))
601
			return(JS_FALSE);
deuce's avatar
deuce committed
602
	}
603
604
605

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

606
607
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
608
609
610
611

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

612
		method = JS_NewObject(cx, NULL, NULL, method_array);	/* exception here June-7-2003 */
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646

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

647
		if(funcs[i].ver) {
648
			if((ver=funcs[i].ver) < 10000)		/* auto convert 313 to 31300 */
649
650
				ver*=100;
			val = INT_TO_JSVAL(ver);
651
652
653
			JS_SetProperty(cx,method, "ver", &val);
		}

654
655
656
657
658
659
660
661
		val=OBJECT_TO_JSVAL(method);
		if(!JS_SetElement(cx, method_array, len+i, &val))
			return(JS_FALSE);
	}

	return(JS_TRUE);
}

662
663
664
665
666
/*
 * Always resolve all here since
 * 1) We'll always be enumerating anyways
 * 2) The speed penalty won't be seen in production code anyways
 */
667
668
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
669
670
671
{
	JSBool	ret=JS_TRUE;

deuce's avatar
deuce committed
672
	if(props) {
673
674
		if(!js_DefineSyncProperties(cx, obj, props))
			ret=JS_FALSE;
deuce's avatar
deuce committed
675
	}
676
		
deuce's avatar
deuce committed
677
	if(funcs) {
deuce's avatar
deuce committed
678
		if(!js_DefineSyncMethods(cx, obj, funcs))
679
			ret=JS_FALSE;
deuce's avatar
deuce committed
680
	}
681

deuce's avatar
deuce committed
682
683
684
685
	if(consts) {
		if(!js_DefineConstIntegers(cx, obj, consts, flags))
			ret=JS_FALSE;
	}
686

687
688
689
	return(ret);
}

690
#else // NON-JSDOCS
691

692
693
694
695
696
697
698
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint i;

	for(i=0;props[i].name;i++) 
		if(!JS_DefinePropertyWithTinyId(cx, obj, 
699
			props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
700
701
702
703
704
705
			return(JS_FALSE);

	return(JS_TRUE);
}


706
JSBool 
deuce's avatar
deuce committed
707
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
708
{
709
	uint i;
710
711

	for(i=0;funcs[i].name;i++)
712
713
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
714
715
716
	return(JS_TRUE);
}

717
718
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
719
720
{
	uint i;
721
	jsval	val;
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743

	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);
			}
		}
	}
744
	if(consts) {
deuce's avatar
deuce committed
745
		for(i=0;consts[i].name;i++) {
746
			if(name==NULL || strcmp(name, consts[i].name)==0) {
747
				val=INT_TO_JSVAL(consts[i].val);
748

deuce's avatar
deuce committed
749
				if(!JS_DefineProperty(cx, obj, consts[i].name, val ,NULL, NULL, flags))
750
751
752
753
754
755
756
					return(JS_FALSE);

				if(name)
					return(JS_TRUE);
			}
		}
	}
757
758
759
760

	return(JS_TRUE);
}

761
762
#endif

763
764
765
766
767
768
769
770
/* 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++) {
771
		val=INT_TO_JSVAL(ints[i].val);
772
773
774
775

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

777
778
779
	return(JS_TRUE);
}

780
static JSBool
781
js_log(JSContext *cx, uintN argc, jsval *arglist)
782
{
783
	jsval *argv=JS_ARGV(cx, arglist);
784
785
    uintN		i=0;
	int32		level=LOG_INFO;
786
    JSString*	str=NULL;
787
	sbbs_t*		sbbs;
788
	jsrefcount	rc;
deuce's avatar
deuce committed
789
790
	char		*line=NULL;
	size_t		line_sz=0;
791

792
793
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

794
795
796
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

797
798
799
800
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
801
802

    for(; i<argc; i++) {
rswindell's avatar
rswindell committed
803
804
		if((str=JS_ValueToString(cx, argv[i]))==NULL) {
			FREE_AND_NULL(line);
deuce's avatar
deuce committed
805
			return(JS_FALSE);
rswindell's avatar
rswindell committed
806
		}
deuce's avatar
deuce committed
807
		JSSTRING_TO_RASTRING(cx, str, line, &line_sz, NULL);
deuce's avatar
deuce committed
808
		if(line==NULL)
809
		    return(JS_FALSE);
810
		rc=JS_SUSPENDREQUEST(cx);
811
		if(sbbs->online==ON_LOCAL) {
deuce's avatar
deuce committed
812
813
814
			if(startup!=NULL && startup->event_lputs!=NULL && level <= startup->log_level) {
				startup->event_lputs(startup->event_cbdata,level,line);
			}
815
		} else
deuce's avatar
deuce committed
816
			lprintf(level,"Node %d %s", sbbs->cfg.node_num, line);
817
		JS_RESUMEREQUEST(cx, rc);
818
	}
rswindell's avatar
rswindell committed
819
820
	if(line != NULL)
		free(line);
821

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

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

838
839
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

851
	rc=JS_SUSPENDREQUEST(cx);
852
	len=RingBufRead(&sbbs->inbuf,buf,len);
853
	JS_RESUMEREQUEST(cx, rc);
854
855

	if(len>0)
856
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len)));
857
858
859
860
861
862

	free(buf);
	return(JS_TRUE);
}

static JSBool
863
js_readln(JSContext *cx, uintN argc, jsval *arglist)
864
{
865
	jsval *argv=JS_ARGV(cx, arglist);
866
867
868
	char*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
869
	jsrefcount	rc;
870

871
872
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

876
877
878
879
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
880
881
882
883

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

884
	rc=JS_SUSPENDREQUEST(cx);
885
	len=sbbs->getstr(buf,len,K_NONE);
886
	JS_RESUMEREQUEST(cx, rc);
887
888

	if(len>0)
889
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf)));
890
891
892
893
894

	free(buf);
	return(JS_TRUE);
}

895
static JSBool
896
js_write(JSContext *cx, uintN argc, jsval *arglist)
897
{
898
	jsval *argv=JS_ARGV(cx, arglist);
899
    uintN		i;
900
    JSString*	str=NULL;
901
	sbbs_t*		sbbs;
902
	jsrefcount	rc;
903
904
	char		*cstr=NULL;
	size_t		cstr_sz=0;
905

906
907
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

908
909
910
911
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
912
913
		if((str=JS_ValueToString(cx, argv[i]))==NULL) {
			FREE_AND_NULL(cstr);
deuce's avatar
deuce committed
914
			return(JS_FALSE);
915
		}
deuce's avatar
deuce committed
916
		JSSTRING_TO_RASTRING(cx, str, cstr, &cstr_sz, NULL);
deuce's avatar
deuce committed
917
		if(cstr==NULL)
918
		    return(JS_FALSE);
919
		rc=JS_SUSPENDREQUEST(cx);
920
		if(sbbs->online==ON_LOCAL)
deuce's avatar
deuce committed
921
			eprintf(LOG_INFO,"%s",cstr);
922
		else
deuce's avatar
deuce committed
923
			sbbs->bputs(cstr);
924
		JS_RESUMEREQUEST(cx, rc);
925
	}
rswindell's avatar
rswindell committed
926
	FREE_AND_NULL(cstr);
927
928

	if(str==NULL)
929
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
930
	else
931
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
932
933
934
    return(JS_TRUE);
}

935
static JSBool
936
js_write_raw(JSContext *cx, uintN argc, jsval *arglist)
937
{
938
	jsval *argv=JS_ARGV(cx, arglist);
939
    uintN		i;
deuce's avatar
deuce committed
940
941
    char*		str=NULL;
    size_t		str_sz=0;
942
943
	size_t		len;
	sbbs_t*		sbbs;
944
	jsrefcount	rc;
945

946
947
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

    for (i = 0; i < argc; i++) {
deuce's avatar
deuce committed
952
		JSVALUE_TO_RASTRING(cx, argv[i], str, &str_sz, &len);
953
		if(str==NULL)
954
		    return(JS_FALSE);
955
956
		if(len < 1)
			continue;
957
		rc=JS_SUSPENDREQUEST(cx);
958
		sbbs->putcom(str, len);
959
		JS_RESUMEREQUEST(cx, rc);
960
	}
rswindell's avatar
rswindell committed
961
962
	if (str != NULL)
		free(str);
963
964
965
966

    return(JS_TRUE);
}

967
static JSBool
968
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
969
970
{
	sbbs_t*		sbbs;
971
	jsrefcount	rc;
972

973
974
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

975
976
977
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

978
	js_write(cx,argc,arglist);
979
	rc=JS_SUSPENDREQUEST(cx);
980
981
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);
982
	JS_RESUMEREQUEST(cx, rc);
983
984
985
986
987

    return(JS_TRUE);
}

static JSBool
988
js_printf(JSContext *cx, uintN argc, jsval *arglist)
989
{
990
	jsval *argv=JS_ARGV(cx, arglist);
991
992
	char*		p;
	sbbs_t*		sbbs;
993
	jsrefcount	rc;
994

995
996
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

997
998
999
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

1000
1001
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
1002
1003
1004
		return(JS_FALSE);
	}

1005
	rc=JS_SUSPENDREQUEST(cx);
1006
	if(sbbs->online==ON_LOCAL)
1007
		eprintf(LOG_INFO,"%s",p);
1008
1009
	else
		sbbs->bputs(p);
1010
	JS_RESUMEREQUEST(cx, rc);
1011

1012
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
1013

1014
	js_sprintf_free(p);
1015
1016
1017
1018
1019

    return(JS_TRUE);
}

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

1027
1028
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

1036
	rc=JS_SUSPENDREQUEST(cx);
1037
1038
1039
1040
1041
1042
1043
1044
	if(sbbs->online==ON_LOCAL)
		eprintf(LOG_WARNING, "%s", cstr);