main.cpp 172 KB
Newer Older
1
/* Synchronet terminal server thread and related functions */
2 3 4 5 6

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *																			*
 * 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										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

#include "sbbs.h"
#include "ident.h"
24
#include "telnet.h"
25
#include "netwrap.h"
26
#include "petdefs.h"
27
#include "js_rtpool.h"
28
#include "js_request.h"
29
#include "ssl.h"
30
#include "ver.h"
deuce's avatar
deuce committed
31 32
#include <multisock.h>
#include <limits.h>		// HOST_NAME_MAX
33 34 35 36 37

#ifdef __unix__
	#include <sys/un.h>
#endif

38
//#define SBBS_TELNET_ENVIRON_SUPPORT 1
39 40
//---------------------------------------------------------------------------

41
#define TELNET_SERVER "Synchronet Terminal Server"
42 43 44 45
#define STATUS_WFC	"Listening"

#define TIMEOUT_THREAD_WAIT		60			// Seconds (was 15)
#define IO_THREAD_BUF_SIZE	   	20000		// Bytes
46
#define TIMEOUT_MUTEX_FILE		12*60*60
47

48
#ifdef USE_CRYPTLIB
rswindell's avatar
rswindell committed
49 50 51 52 53 54 55 56 57 58 59

	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 {
60
			uint32_t remain = protected_uint32_adjust_fetch(&ssh_sessions, -1);
Deucе's avatar
Deucе committed
61
			lprintf(LOG_DEBUG, "%04d SSH Cryptlib Session: %d destroyed from line %d (%u remain)"
rswindell's avatar
rswindell committed
62 63 64 65 66 67 68 69
				, 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__);	\
70
			sbbs->ssh_mode = false;									\
rswindell's avatar
rswindell committed
71 72
			pthread_mutex_unlock(&sbbs->ssh_mutex);					\
		}															\
deuce's avatar
deuce committed
73
	} while(0)
74
#else
rswindell's avatar
rswindell committed
75
	#define	SSH_END(x)
76 77
#endif

78 79
volatile time_t	uptime=0;
volatile ulong	served=0;
80

81
static	protected_uint32_t node_threads_running;
82

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

108 109 110 111 112 113
/* convenient space-saving global variables */
extern "C" {
const char* crlf="\r\n";
const char* nulstr="";
};

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

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

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

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


155 156 157 158
extern "C" {

static bbs_startup_t* startup=NULL;

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

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

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

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

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

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

199
int lputs(int level, const char* str)
200
{
201
	if(level <= LOG_ERR) {
202 203
		char errmsg[1024];
		SAFEPRINTF(errmsg, "term %s", str);
204
		errorlog(&scfg, level, startup==NULL ? NULL:startup->host_name, errmsg);
205
		if(startup!=NULL && startup->errormsg!=NULL)
206
			startup->errormsg(startup->cbdata,level,errmsg);
207
	}
208

209
	if(startup==NULL || startup->lputs==NULL || str==NULL || level > startup->log_level)
210 211
    	return(0);

212 213 214 215 216
#if defined(_WIN32)
	if(IsBadCodePtr((FARPROC)startup->lputs))
		return(0);
#endif

217
    return(startup->lputs(startup->cbdata,level,str));
218 219
}

220 221
int eputs(int level, const char *str)
{
222 223 224
	if(*str == 0)
		return 0;

225
	if(level <= LOG_ERR) {
226 227
		char errmsg[1024];
		SAFEPRINTF(errmsg, "evnt %s", str);
228
		errorlog(&scfg, level, startup==NULL ? NULL:startup->host_name, errmsg);
229
		if(startup!=NULL && startup->errormsg!=NULL)
230
			startup->errormsg(startup->cbdata, level, errmsg);
231 232
	}

Deucе's avatar
Deucе committed
233 234
	if(startup==NULL || startup->event_lputs==NULL || level > startup->log_level)
		return(0);
235

Deucе's avatar
Deucе committed
236
	return(startup->event_lputs(startup->event_cbdata,level,str));
237 238
}

239
int lprintf(int level, const char *fmt, ...)
240 241 242 243
{
	va_list argptr;
	char sbuf[1024];

Deucе's avatar
EVN MR!  
Deucе committed
244 245
	va_start(argptr,fmt);
	vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
246
	sbuf[sizeof(sbuf)-1]=0;
Deucе's avatar
EVN MR!  
Deucе committed
247 248
	va_end(argptr);
	return(lputs(level,sbuf));
249 250
}

251
/* Picks the right log callback function (event or term) based on the sbbs->cfg.node_num value */
252
/* Prepends the current node number and user alias (if applicable) */
253 254
int sbbs_t::lputs(int level, const char* str)
{
255 256 257 258 259 260 261 262 263 264 265
	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)
266
		SAFEPRINTF(user_str, "<%s> ", useron.alias);
267
	SAFEPRINTF3(msg, "%s%s%s", prefix, user_str, str);
268
	strip_ctrl(msg, msg);
269 270 271
	if(is_event_thread)
		return ::eputs(level, msg);
	return ::lputs(level, msg);
272 273
}

274 275 276 277 278 279 280 281 282 283 284 285
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
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
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);
}

310 311
void call_socket_open_callback(BOOL open)
{
312
	if(startup!=NULL && startup->socket_open!=NULL)
313 314 315
		startup->socket_open(startup->cbdata, open);
}

316
SOCKET open_socket(int domain, int type, const char* protocol)
317 318 319 320
{
	SOCKET	sock;
	char	error[256];

321
	sock=socket(domain, type, IPPROTO_IP);
322 323
	if(sock!=INVALID_SOCKET)
		call_socket_open_callback(TRUE);
324
	if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
325
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
326 327 328 329

	return(sock);
}

330
// Used by sbbs_t::ftp_put() and js_accept()
deuce's avatar
deuce committed
331
SOCKET accept_socket(SOCKET s, union xp_sockaddr* addr, socklen_t* addrlen)
332 333 334
{
	SOCKET	sock;

deuce's avatar
deuce committed
335
	sock=accept(s,&addr->addr,addrlen);
336 337
	if(sock!=INVALID_SOCKET)
		call_socket_open_callback(TRUE);
338 339 340 341 342 343 344 345 346 347 348 349 350

	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);
351
	call_socket_open_callback(FALSE);
352
	if(result!=0 && ERROR_VALUE!=ENOTSOCK)
353
		lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
354 355 356
	return(result);
}

deuce's avatar
deuce committed
357
/* TODO: IPv6 */
358 359 360 361 362 363
u_long resolve_ip(char *addr)
{
	HOSTENT*	host;
	char*		p;

	if(*addr==0)
364
		return((u_long)INADDR_NONE);
365 366

	for(p=addr;*p;p++)
367
		if(*p!='.' && !IS_DIGIT(*p))
368 369 370
			break;
	if(!(*p))
		return(inet_addr(addr));
371
	if((host=gethostbyname(addr))==NULL)
372
		return((u_long)INADDR_NONE);
373 374 375 376 377
	return(*((ulong*)host->h_addr_list[0]));
}

} /* extern "C" */

378 379 380 381 382 383 384 385 386 387 388
#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) {
389
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
390 391 392 393
		WSAInitialized=TRUE;
		return(TRUE);
	}

394
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
395 396 397 398 399 400 401 402 403 404
	return(FALSE);
}

#else /* No WINSOCK */

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

#endif

Rob Swindell's avatar
Rob Swindell committed
405
DLLEXPORT void sbbs_srand()
406
{
407
	DWORD seed;
408

409
	xp_randomize();
410
#if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
411
	int     rf,rd=0;
412

413 414
	if((rf=open(RANDOM_DEV, O_RDONLY|O_NONBLOCK))!=-1) {
		rd=read(rf, &seed, sizeof(seed));
415 416
		close(rf);
	}
deuce's avatar
deuce committed
417
	if (rd != sizeof(seed))
418
#endif
419
		seed = time32(NULL) ^ (uintmax_t)GetCurrentThreadId();
420 421

 	srand(seed);
422 423 424
	sbbs_random(10);	/* Throw away first number */
}

Rob Swindell's avatar
Rob Swindell committed
425
int sbbs_random(int n)
426 427 428 429
{
	return(xp_random(n));
}

430 431
#ifdef JAVASCRIPT

432 433
static js_server_props_t js_server_props;

Rob Swindell's avatar
Rob Swindell committed
434
void* js_GetClassPrivate(JSContext *cx, JSObject *obj, JSClass* cls)
435 436 437
{
	void *ret = JS_GetInstancePrivate(cx, obj, cls, NULL);

438 439 440 441
	/*
	 * NOTE: Any changes here should also be added to the same function in jsdoor.c
	 *       (ie: anything not Synchronet specific).
	 */
442 443 444 445 446 447
	if(ret == NULL)
		JS_ReportError(cx, "'%s' instance: No Private Data or Class Mismatch"
			, cls == NULL ? "???" : cls->name);
	return ret;
}

448
JSBool
Rob Swindell's avatar
Rob Swindell committed
449
js_CreateArrayOfStrings(JSContext* cx, JSObject* parent, const char* name, const char* str[],uintN flags)
450 451 452 453 454 455
{
	JSObject*	array;
	JSString*	js_str;
	jsval		val;
	size_t		i;
	jsuint		len=0;
456

457 458 459
	if(JS_GetProperty(cx,parent,name,&val) && val!=JSVAL_VOID)
		array=JSVAL_TO_OBJECT(val);
	else
460 461
		if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)	/* Assertion here, in _heap_alloc_dbg, June-21-2004 */
			return(JS_FALSE);								/* Caused by nntpservice.js? */
462

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

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

478
	return(JS_TRUE);
479 480
}

481
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
482 483

JSBool
Rob Swindell's avatar
Rob Swindell committed
484
js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
485 486 487 488 489 490
{
	JSString* js_str = JS_NewStringCopyZ(cx, str);

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

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

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

JSBool
Rob Swindell's avatar
Rob Swindell committed
501
js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
502 503 504 505 506 507 508 509 510 511
{
	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));
}

512
#ifdef BUILD_JSDOCS
513 514

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

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

536
JSBool
Rob Swindell's avatar
Rob Swindell committed
537
js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
538 539
{
	uint		i;
540
	long		ver;
541 542 543 544 545 546 547 548 549 550 551 552
	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++) {
553 554 555 556 557 558 559 560 561
		if (props[i].tinyid < 256 && props[i].tinyid > -129) {
			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))
				return(JS_FALSE);
		}
		else {
			if(!JS_DefineProperty(cx, obj, props[i].name, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
				return(JS_FALSE);
		}
562
		if(props[i].flags&JSPROP_ENUMERATE) {	/* No need to version invisible props */
563 564 565
			if((ver=props[i].ver) < 10000)		/* auto convert 313 to 31300 */
				ver*=100;
			val = INT_TO_JSVAL(ver);
566 567 568 569 570 571 572 573
			if(!JS_SetElement(cx, array, len++, &val))
				return(JS_FALSE);
		}
	}

	return(JS_TRUE);
}

574
JSBool
Rob Swindell's avatar
Rob Swindell committed
575
js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
576 577 578
{
	int			i;
	jsuint		len=0;
579
	long		ver;
580 581 582 583
	jsval		val;
	JSObject*	method;
	JSObject*	method_array;
	JSString*	js_str;
deuce's avatar
deuce committed
584 585
	size_t		str_len=0;
	char		*str=NULL;
586 587

	/* Return existing method_list array if it's already been created */
deuce's avatar
deuce committed
588
	if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID) {
589
		method_array=JSVAL_TO_OBJECT(val);
deuce's avatar
deuce committed
590 591 592
		// If the first item is already in the list, don't do anything.
		if(!JS_GetArrayLength(cx, method_array, &len))
			return(JS_FALSE);
593
		for(i=0; i<(int)len; i++) {
deuce's avatar
deuce committed
594 595 596 597 598 599 600 601 602 603 604
			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 {
605
		if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL)
606
			return(JS_FALSE);
deuce's avatar
deuce committed
607 608
		if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
				, NULL, NULL, 0))
609
			return(JS_FALSE);
deuce's avatar
deuce committed
610
	}
611 612 613

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

614 615
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
616 617 618 619

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

620
		method = JS_NewObject(cx, NULL, NULL, method_array);	/* exception here June-7-2003 */
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 647

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

648
		if(funcs[i].desc!=NULL) {
649 650 651 652 653 654
			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);
		}

655
		if(funcs[i].ver) {
656
			if((ver=funcs[i].ver) < 10000)		/* auto convert 313 to 31300 */
657 658
				ver*=100;
			val = INT_TO_JSVAL(ver);
659 660 661
			JS_SetProperty(cx,method, "ver", &val);
		}

662 663 664 665 666 667 668 669
		val=OBJECT_TO_JSVAL(method);
		if(!JS_SetElement(cx, method_array, len+i, &val))
			return(JS_FALSE);
	}

	return(JS_TRUE);
}

670 671 672 673 674
/*
 * Always resolve all here since
 * 1) We'll always be enumerating anyways
 * 2) The speed penalty won't be seen in production code anyways
 */
675
JSBool
Rob Swindell's avatar
Rob Swindell committed
676
js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
677 678 679
{
	JSBool	ret=JS_TRUE;

deuce's avatar
deuce committed
680
	if(props) {
681 682
		if(!js_DefineSyncProperties(cx, obj, props))
			ret=JS_FALSE;
deuce's avatar
deuce committed
683
	}
684

deuce's avatar
deuce committed
685
	if(funcs) {
deuce's avatar
deuce committed
686
		if(!js_DefineSyncMethods(cx, obj, funcs))
687
			ret=JS_FALSE;
deuce's avatar
deuce committed
688
	}
689

deuce's avatar
deuce committed
690 691 692 693
	if(consts) {
		if(!js_DefineConstIntegers(cx, obj, consts, flags))
			ret=JS_FALSE;
	}
694

695 696 697
	return(ret);
}

698
#else // NON-JSDOCS
699

700
JSBool
Rob Swindell's avatar
Rob Swindell committed
701
js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
702 703 704
{
	uint i;

705 706 707 708
	/*
	 * NOTE: Any changes here should also be added to the same function in jsdoor.c
	 *       (ie: anything not Synchronet specific).
	 */
709 710 711 712 713 714 715 716 717 718 719
	for(i=0;props[i].name;i++) {
		if (props[i].tinyid < 256 && props[i].tinyid > -129) {
			if(!JS_DefinePropertyWithTinyId(cx, obj,
			    props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
				return(JS_FALSE);
		}
		else {
			if(!JS_DefineProperty(cx, obj, props[i].name, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
				return(JS_FALSE);
		}
	}
720 721 722 723 724

	return(JS_TRUE);
}


725
JSBool
Rob Swindell's avatar
Rob Swindell committed
726
js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
727
{
728
	uint i;
729

730 731 732 733
	/*
	 * NOTE: Any changes here should also be added to the same function in jsdoor.c
	 *       (ie: anything not Synchronet specific).
	 */
734
	for(i=0;funcs[i].name;i++)
735 736
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
737 738 739
	return(JS_TRUE);
}

740
JSBool
Rob Swindell's avatar
Rob Swindell committed
741
js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
742 743
{
	uint i;
744
	jsval	val;
745

746 747 748 749
	/*
	 * NOTE: Any changes here should also be added to the same function in jsdoor.c
	 *       (ie: anything not Synchronet specific).
	 */
750 751 752
	if(props) {
		for(i=0;props[i].name;i++) {
			if(name==NULL || strcmp(name, props[i].name)==0) {
753 754 755 756 757 758 759 760 761
				if (props[i].tinyid < 256 && props[i].tinyid > -129) {
					if(!JS_DefinePropertyWithTinyId(cx, obj,
					    props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
						return(JS_FALSE);
				}
				else {
					if(!JS_DefineProperty(cx, obj, props[i].name, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
						return(JS_FALSE);
				}
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
				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);
			}
		}
	}
777
	if(consts) {
deuce's avatar
deuce committed
778
		for(i=0;consts[i].name;i++) {
779
			if(name==NULL || strcmp(name, consts[i].name)==0) {
780
				val=INT_TO_JSVAL(consts[i].val);
781

deuce's avatar
deuce committed
782
				if(!JS_DefineProperty(cx, obj, consts[i].name, val ,NULL, NULL, flags))
783 784 785 786 787 788 789
					return(JS_FALSE);

				if(name)
					return(JS_TRUE);
			}
		}
	}
790 791 792 793

	return(JS_TRUE);
}

794 795
#endif

796 797
/* This is a stream-lined version of JS_DefineConstDoubles */
JSBool
Rob Swindell's avatar
Rob Swindell committed
798
js_DefineConstIntegers(JSContext* cx, JSObject* obj, jsConstIntSpec* ints, int flags)
799 800 801 802 803
{
	uint	i;
	jsval	val;

	for(i=0;ints[i].name;i++) {
804
		val=INT_TO_JSVAL(ints[i].val);
805 806 807 808

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

810 811 812
	return(JS_TRUE);
}

813
static JSBool
814
js_log(JSContext *cx, uintN argc, jsval *arglist)
815
{
816
	jsval *argv=JS_ARGV(cx, arglist);
817 818
    uintN		i=0;
	int32		level=LOG_INFO;
819
    JSString*	str=NULL;
820
	sbbs_t*		sbbs;
821
	jsrefcount	rc;
deuce's avatar
deuce committed
822 823
	char		*line=NULL;
	size_t		line_sz=0;
824

825 826
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

827 828 829
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

830 831 832 833
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
834 835

    for(; i<argc; i++) {
rswindell's avatar
rswindell committed
836 837
		if((str=JS_ValueToString(cx, argv[i]))==NULL) {
			FREE_AND_NULL(line);
deuce's avatar
deuce committed
838
			return(JS_FALSE);
rswindell's avatar
rswindell committed
839
		}
deuce's avatar
deuce committed
840
		JSSTRING_TO_RASTRING(cx, str, line, &line_sz, NULL);
deuce's avatar
deuce committed
841
		if(line==NULL)
842
		    return(JS_FALSE);
843
		rc=JS_SUSPENDREQUEST(cx);
844
		sbbs->lputs(level, line);
845
		JS_RESUMEREQUEST(cx, rc);
846
	}
rswindell's avatar
rswindell committed
847 848
	if(line != NULL)
		free(line);
849

850
	if(str==NULL)
851
		JS_SET_RVAL(cx, arglist, JSVAL_VOID);
852
	else
853
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
854 855 856
    return(JS_TRUE);
}

857
static JSBool
858
js_read(JSContext *cx, uintN argc, jsval *arglist)
859
{
860
	jsval *argv=JS_ARGV(cx, arglist);
861 862 863
	uchar*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
864
	jsrefcount	rc;
865

866 867
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

868 869 870
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

871 872 873 874
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
875 876 877 878

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

879
	rc=JS_SUSPENDREQUEST(cx);
880
	len=RingBufRead(&sbbs->inbuf,buf,len);
881
	JS_RESUMEREQUEST(cx, rc);
882 883

	if(len>0)
884
		JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len)));
885 886 887 888 889 890

	free(buf);
	return(JS_TRUE);
}

static JSBool
891
js_readln(JSContext *cx, uintN argc, jsval *arglist)
892
{
893
	jsval *argv=JS_ARGV(cx, arglist);
894 895 896
	char*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
897
	jsrefcount	rc;
898

899 900
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

901 902 903
	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

904 905 906 907
	if(argc) {
		if(!JS_ValueToInt32(cx,argv[0],&len))
			return JS_FALSE;
	}
908 909 910 911

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

912
	rc=JS_SUSPENDREQUEST(cx);
913
	len=sbbs->getstr(buf,len,K_NONE);
914
	JS_RESUMEREQUEST(cx, rc);