main.cpp 129 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/* main.cpp */

/* Synchronet main/telnet server thread and related functions */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
11
 * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html		*
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
40
41
42
43
 *																			*
 * 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" 

#ifdef __unix__
	#include <sys/un.h>
deuce's avatar
deuce committed
44
45
46
47
	#ifndef SUN_LEN
		#define SUN_LEN(su) \
	        (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
	#endif
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#endif

//---------------------------------------------------------------------------

#define TELNET_SERVER "Synchronet Telnet Server"
#define STATUS_WFC	"Listening"

#define TIMEOUT_THREAD_WAIT		60			// Seconds (was 15)
#define IO_THREAD_BUF_SIZE	   	20000		// Bytes

// 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

time_t	uptime=0;
DWORD	served=0;

73
74
static	ulong node_threads_running=0;
static	ulong thread_count=0;
75
76
77
78
79
80
81
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];
static	SOCKET telnet_socket=INVALID_SOCKET;
static	SOCKET rlogin_socket=INVALID_SOCKET;
static	sbbs_t*	sbbs=NULL;
static	scfg_t	scfg;
static	char *	text[TOTAL_TEXT];
static	WORD	first_node;
static	WORD	last_node;
90
static	bool	terminate_server=false;
91
92
static	str_list_t recycle_semfiles;
static	str_list_t shutdown_semfiles;
93
94
95
96
97
98
99
100

extern "C" {

static bbs_startup_t* startup=NULL;

static void status(char* str)
{
	if(startup!=NULL && startup->status!=NULL)
101
	    startup->status(startup->cbdata,str);
102
103
104
105
106
}

static void update_clients()
{
	if(startup!=NULL && startup->clients!=NULL)
107
		startup->clients(startup->cbdata,node_threads_running);
108
109
110
111
112
}

void client_on(SOCKET sock, client_t* client, BOOL update)
{
	if(startup!=NULL && startup->client_on!=NULL)
113
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
114
115
116
117
118
}

static void client_off(SOCKET sock)
{
	if(startup!=NULL && startup->client_on!=NULL)
119
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
120
121
122
123
124
125
}

static void thread_up(BOOL setuid)
{
	thread_count++;
	if(startup!=NULL && startup->thread_up!=NULL)
126
		startup->thread_up(startup->cbdata,TRUE,setuid);
127
128
129
130
131
132
133
}

static void thread_down()
{
	if(thread_count>0)
		thread_count--;
	if(startup!=NULL && startup->thread_up!=NULL)
134
		startup->thread_up(startup->cbdata,FALSE,FALSE);
135
136
}

137
int lputs(int level, char* str)
138
{
rswindell's avatar
rswindell committed
139
	if(startup==NULL || startup->lputs==NULL || str==NULL)
140
141
    	return(0);

142
    return(startup->lputs(startup->cbdata,level,str));
143
144
}

145
int lprintf(int level, char *fmt, ...)
146
147
148
149
150
151
152
153
{
	va_list argptr;
	char sbuf[1024];

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
154
    return(lputs(level,sbuf));
155
156
}

157
int eprintf(int level, char *fmt, ...)
158
159
160
161
{
	va_list argptr;
	char sbuf[1024];

162
    if(startup==NULL || startup->event_lputs==NULL)
163
164
165
166
167
168
169
        return(0);

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
	strip_ctrl(sbuf);
170
    return(startup->event_lputs(level,sbuf));
171
172
}

173
SOCKET open_socket(int type, const char* protocol)
174
175
176
177
178
179
{
	SOCKET	sock;
	char	error[256];

	sock=socket(AF_INET, type, IPPROTO_IP);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
180
		startup->socket_open(startup->cbdata,TRUE);
181
	if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
182
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
183
184
185
186
187
188
189
190
191
192

	return(sock);
}

SOCKET accept_socket(SOCKET s, SOCKADDR* addr, socklen_t* addrlen)
{
	SOCKET	sock;

	sock=accept(s,addr,addrlen);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
193
		startup->socket_open(startup->cbdata,TRUE);
194
195
196
197
198
199
200
201
202
203
204
205
206

	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);
207
	if(startup!=NULL && startup->socket_open!=NULL)
208
		startup->socket_open(startup->cbdata,FALSE);
209
	if(result!=0 && ERROR_VALUE!=ENOTSOCK)
210
		lprintf(LOG_ERR,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
211
212
213
214
215
216
217
218
219
220
	return(result);
}


u_long resolve_ip(char *addr)
{
	HOSTENT*	host;
	char*		p;

	if(*addr==0)
221
		return((u_long)INADDR_NONE);
222
223
224
225
226
227
228

	for(p=addr;*p;p++)
		if(*p!='.' && !isdigit(*p))
			break;
	if(!(*p))
		return(inet_addr(addr));
	if((host=gethostbyname(addr))==NULL) 
229
		return((u_long)INADDR_NONE);
230
231
232
233
234
	return(*((ulong*)host->h_addr_list[0]));
}

} /* extern "C" */

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
#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) {
		lprintf(LOG_INFO,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
		WSAInitialized=TRUE;
		return(TRUE);
	}

    lprintf(LOG_ERR,"!WinSock startup ERROR %d", status);
	return(FALSE);
}

#else /* No WINSOCK */

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

#endif

262
263
DLLEXPORT void DLLCALL sbbs_srand()
{
264
265
266
267
268
269
270
271
272
273
274
275
	DWORD seed = time(NULL) ^ (DWORD)GetCurrentThreadId();

#if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
	int     rf;

	if((rf=open(RANDOM_DEV, O_RDONLY))!=-1) {
		read(rf, &seed, sizeof(seed));
		close(rf);
	}
#endif

 	srand(seed);
276
277
278
279
280
281
282
283
	sbbs_random(10);	/* Throw away first number */
}

DLLEXPORT int DLLCALL sbbs_random(int n)
{
	return(xp_random(n));
}

284
285
#ifdef JAVASCRIPT

286
287
static js_server_props_t js_server_props;

288
289
290
291
292
293
294
295
296
297
298
299
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
300
301
		if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)	/* Assertion here, in _heap_alloc_dbg, June-21-2004 */
			return(JS_FALSE);								/* Caused by nntpservice.js? */
302

303
304
305
306
	if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
		,NULL,NULL,flags))
		return(JS_FALSE);

307
308
309
310
311
312
313
314
315
316
317
	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;
	}

318
	return(JS_TRUE);
319
320
}

321
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
322
323

JSBool
324
DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
325
326
327
328
329
330
331
{
	JSString* js_str = JS_NewStringCopyZ(cx, str);

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

	return(JS_DefineProperty(cx,obj,"_description"
332
333
334
			,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
		&& JS_DefineProperty(cx,obj,"_ver"
			,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
335
336
337
}

JSBool
338
DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
339
340
341
342
343
344
345
346
347
348
{
	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));
}

349
#ifdef BUILD_JSDOCS
350
351

static const char* method_array_name = "_method_list";
352
static const char* propver_array_name = "_property_ver_list";
353
354
355
356
357
358
359
360
361
362
363
364
365

/*
 * 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",
366
367
	"null",
	"xml object",
368
369
370
	"array",
	"alias",
	"undefined"
371
372
};

373
374
375
376
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint		i;
377
	long		ver;
378
379
380
381
382
383
384
385
386
387
388
389
	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++) {
390
391
		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))
392
393
			return(JS_FALSE);
		if(props[i].flags&JSPROP_ENUMERATE) {	/* No need to version invisible props */
394
395
396
			if((ver=props[i].ver) < 10000)		/* auto convert 313 to 31300 */
				ver*=100;
			val = INT_TO_JSVAL(ver);
397
398
399
400
401
402
403
404
			if(!JS_SetElement(cx, array, len++, &val))
				return(JS_FALSE);
		}
	}

	return(JS_TRUE);
}

405
JSBool 
406
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
407
408
409
{
	int			i;
	jsuint		len=0;
410
	long		ver;
411
412
413
414
415
416
417
418
419
420
421
422
	jsval		val;
	JSObject*	method;
	JSObject*	method_array;
	JSString*	js_str;

	/* Return existing method_list array if it's already been created */
	if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID)
		method_array=JSVAL_TO_OBJECT(val);
	else
		if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL) 
			return(JS_FALSE);

423
424
425
426
	if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
		, NULL, NULL, 0))
		return(JS_FALSE);

427
428
429
430
431
432
	if(append)
		if(!JS_GetArrayLength(cx, method_array, &len))
			return(JS_FALSE);

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

433
434
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
435
436
437
438

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

439
		method = JS_NewObject(cx, NULL, NULL, method_array);	/* exception here June-7-2003 */
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473

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

474
		if(funcs[i].ver) {
475
476
477
			if((ver=funcs[i].ver<10000)		/* auto convert 313 to 31300 */
				ver*=100;
			val = INT_TO_JSVAL(ver);
478
479
480
			JS_SetProperty(cx,method, "ver", &val);
		}

481
482
483
484
485
486
487
488
		val=OBJECT_TO_JSVAL(method);
		if(!JS_SetElement(cx, method_array, len+i, &val))
			return(JS_FALSE);
	}

	return(JS_TRUE);
}

489
#else // NON-JSDOCS
490

491
492
493
494
495
496
497
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint i;

	for(i=0;props[i].name;i++) 
		if(!JS_DefinePropertyWithTinyId(cx, obj, 
498
			props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
499
500
501
502
503
504
			return(JS_FALSE);

	return(JS_TRUE);
}


505
JSBool 
506
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
507
{
508
	uint i;
509
510

	for(i=0;funcs[i].name;i++)
511
512
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
513
514
515
516
517
	return(JS_TRUE);
}

#endif

518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
/* 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++) {
        if(!JS_NewNumberValue(cx, ints[i].val, &val))
			return(JS_FALSE);

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

536
537
538
539
540
541
542
543
544
545
546
547
548
549
char*
DLLCALL js_ValueToStringBytes(JSContext* cx, jsval val, size_t* len)
{
	JSString* str;
	
	if((str=JS_ValueToString(cx, val))==NULL)
		return(NULL);

	if(len!=NULL)
		*len = JS_GetStringLength(str);

	return(JS_GetStringBytes(str));
}

550
551
552
static JSBool
js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
553
554
    uintN		i=0;
	int32		level=LOG_INFO;
555
    JSString*	str=NULL;
556
557
558
559
560
	sbbs_t*		sbbs;

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

561
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i]))
562
563
564
		JS_ValueToInt32(cx,argv[i++],&level);

    for(; i<argc; i++) {
565
566
567
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
		    return(JS_FALSE);
		if(sbbs->online==ON_LOCAL) {
568
			if(startup!=NULL && startup->event_lputs!=NULL)
569
				startup->event_lputs(level,JS_GetStringBytes(str));
570
		} else
571
			lputs(level,JS_GetStringBytes(str));
572
	}
573

574
575
576
577
	if(str==NULL)
		*rval = JSVAL_VOID;
	else
		*rval = STRING_TO_JSVAL(str);
578
579
580
    return(JS_TRUE);
}

581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
static JSBool
js_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	uchar*		buf;
	int32		len=128;
	sbbs_t*		sbbs;

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

	if(argc)
		JS_ValueToInt32(cx,argv[0],&len);

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

	len=RingBufRead(&sbbs->inbuf,buf,len);

	if(len>0)
		*rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len));

	free(buf);
	return(JS_TRUE);
}

static JSBool
js_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*		buf;
	int32		len=128;
	sbbs_t*		sbbs;

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

	if(argc)
		JS_ValueToInt32(cx,argv[0],&len);

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

	len=sbbs->getstr(buf,len,K_NONE);

	if(len>0)
		*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf));

	free(buf);
	return(JS_TRUE);
}

631
static JSBool
632
js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
633
634
{
    uintN		i;
635
    JSString*	str=NULL;
636
637
638
639
640
641
642
643
644
	sbbs_t*		sbbs;

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

    for (i = 0; i < argc; i++) {
		if((str=JS_ValueToString(cx, argv[i]))==NULL)
		    return(JS_FALSE);
		if(sbbs->online==ON_LOCAL)
645
			eprintf(LOG_INFO,"%s",JS_GetStringBytes(str));
646
647
		else
			sbbs->bputs(JS_GetStringBytes(str));
648
649
650
651
652
653
654
655
656
	}

	if(str==NULL)
		*rval = JSVAL_VOID;
	else
		*rval = STRING_TO_JSVAL(str);
    return(JS_TRUE);
}

657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
static JSBool
js_write_raw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    uintN		i;
    char*	str=NULL;
	size_t		len;
	sbbs_t*		sbbs;

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

    for (i = 0; i < argc; i++) {
		if((str=js_ValueToStringBytes(cx, argv[i], &len))==NULL)
		    return(JS_FALSE);
		sbbs->putcom(str, len);
	}

    return(JS_TRUE);
}

677
static JSBool
678
js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
679
680
681
682
683
684
685
{
	sbbs_t*		sbbs;

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

	js_write(cx,obj,argc,argv,rval);
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);

    return(JS_TRUE);
}

static JSBool
js_printf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*		p;
	sbbs_t*		sbbs;

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

701
702
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
703
704
705
706
		return(JS_FALSE);
	}

	if(sbbs->online==ON_LOCAL)
707
		eprintf(LOG_INFO,"%s",p);
708
709
	else
		sbbs->bputs(p);
710
711
712

	*rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p));

713
	js_sprintf_free(p);
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788

    return(JS_TRUE);
}

static JSBool
js_alert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *	str;
	sbbs_t*		sbbs;

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

	if((str=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

	sbbs->attr(sbbs->cfg.color[clr_err]);
	sbbs->bputs(JS_GetStringBytes(str));
	sbbs->attr(LIGHTGRAY);
	sbbs->bputs(crlf);

    return(JS_TRUE);
}

static JSBool
js_confirm(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *	str;
	sbbs_t*		sbbs;

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

	if((str=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

	*rval = BOOLEAN_TO_JSVAL(sbbs->yesno(JS_GetStringBytes(str)));
	return(JS_TRUE);
}

static JSBool
js_prompt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char		instr[81];
    JSString *	prompt;
    JSString *	str;
	sbbs_t*		sbbs;

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

	if((prompt=JS_ValueToString(cx, argv[0]))==NULL)
	    return(JS_FALSE);

	if(argc>1) {
		if((str=JS_ValueToString(cx, argv[1]))==NULL)
		    return(JS_FALSE);
		SAFECOPY(instr,JS_GetStringBytes(str));
	} else
		instr[0]=0;

	sbbs->bprintf("\1n\1y\1h%s\1w: ",JS_GetStringBytes(prompt));

	if(!sbbs->getstr(instr,sizeof(instr)-1,K_EDIT)) {
		*rval = JSVAL_NULL;
		return(JS_TRUE);
	}

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

	*rval = STRING_TO_JSVAL(str);
    return(JS_TRUE);
}

789
static jsSyncMethodSpec js_global_functions[] = {
790
	{"log",				js_log,				1,	JSTYPE_STRING,	JSDOCSTR("[level,] value [,value]")
791
	,JSDOCSTR("add a line of text to the server and/or system log, "
792
793
		"<i>values</i> are typically string constants or variables, "
		"<i>level</i> is the debug level/priority (default: <tt>LOG_INFO</tt>)")
794
	,311
795
	},
796
797
	{"read",			js_read,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read up to count characters from input stream")
798
	,311
799
800
801
	},
	{"readln",			js_readln,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read a single line, up to count characters, from input stream")
802
	,311
803
	},
804
	{"write",			js_write,			0,	JSTYPE_VOID,	JSDOCSTR("value [,value]")
805
	,JSDOCSTR("send one or more values (typically strings) to the server output")
806
	,311
807
	},
808
809
810
811
	{"write_raw",			js_write_raw,			0,	JSTYPE_VOID,	JSDOCSTR("value [,value]")
	,JSDOCSTR("send a stream of bytes (possibly containing NULLs or special control code sequences) to the server output")
	,313
	},
812
813
814
815
	{"print",			js_writeln,			0,	JSTYPE_ALIAS },
    {"writeln",         js_writeln,         0,	JSTYPE_VOID,	JSDOCSTR("value [,value]")
	,JSDOCSTR("send a line of text to the console or event log with automatic line termination (CRLF), "
		"<i>values</i> are typically string constants or variables (AKA print)")
816
	,311
817
	},
818
    {"printf",          js_printf,          1,	JSTYPE_STRING,	JSDOCSTR("string format [,value][,value]")
819
	,JSDOCSTR("print a formatted string - <small>CAUTION: for experienced C programmers ONLY</small>")
820
	,310
821
822
823
	},	
	{"alert",			js_alert,			1,	JSTYPE_VOID,	JSDOCSTR("value")
	,JSDOCSTR("print an alert message (ala client-side JS)")
824
	,310
825
826
827
	},
	{"prompt",			js_prompt,			1,	JSTYPE_STRING,	JSDOCSTR("[value]")
	,JSDOCSTR("displays a prompt (<i>value</i>) and returns a string of user input (ala clent-side JS)")
828
	,310
829
830
831
832
	},
	{"confirm",			js_confirm,			1,	JSTYPE_BOOLEAN,	JSDOCSTR("value")
	,JSDOCSTR("displays a Yes/No prompt and returns <i>true</i> or <i>false</i> "
		"based on users confirmation (ala client-side JS)")
833
	,310
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
	},
    {0}
};

static void
js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
	char	line[64];
	char	file[MAX_PATH+1];
	sbbs_t*	sbbs;
	const char*	warning;

	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return;
	
	if(report==NULL) {
850
		lprintf(LOG_ERR,"!JavaScript: %s", message);
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
		return;
    }

	if(report->filename)
		sprintf(file," %s",report->filename);
	else
		file[0]=0;

	if(report->lineno)
		sprintf(line," line %d",report->lineno);
	else
		line[0]=0;

	if(JSREPORT_IS_WARNING(report->flags)) {
		if(JSREPORT_IS_STRICT(report->flags))
			warning="strict warning";
		else
			warning="warning";
	} else
		warning=nulstr;

	if(sbbs->online==ON_LOCAL) 
873
		eprintf(LOG_ERR,"!JavaScript %s%s%s: %s",warning,file,line,message);
874
	else {
875
		lprintf(LOG_ERR,"!JavaScript %s%s%s: %s",warning,file,line,message);
876
877
878
879
		sbbs->bprintf("!JavaScript %s%s%s: %s\r\n",warning,file,line,message);
	}
}

880
bool sbbs_t::js_init(ulong* stack_frame)
881
882
883
884
885
886
887
888
{
	char		node[128];

    if(cfg.node_num)
    	sprintf(node,"Node %d",cfg.node_num);
    else
    	strcpy(node,client_name);

889
890
891
	if(startup->js.max_bytes==0)			startup->js.max_bytes=JAVASCRIPT_MAX_BYTES;
	if(startup->js.cx_stack==0)				startup->js.cx_stack=JAVASCRIPT_CONTEXT_STACK;

892
	lprintf(LOG_DEBUG,"%s JavaScript: Creating runtime: %lu bytes"
893
		,node,startup->js.max_bytes);
894

895
	if((js_runtime = JS_NewRuntime(startup->js.max_bytes))==NULL)
896
897
		return(false);

898
	lprintf(LOG_DEBUG,"%s JavaScript: Initializing context (stack: %lu bytes)"
899
		,node,startup->js.cx_stack);
900

901
    if((js_cx = JS_NewContext(js_runtime, startup->js.cx_stack))==NULL)
902
		return(false);
rswindell's avatar
rswindell committed
903
904
	
	memset(&js_branch,0,sizeof(js_branch));
905
906
907
	js_branch.limit = startup->js.branch_limit;
	js_branch.gc_interval = startup->js.gc_interval;
	js_branch.yield_interval = startup->js.yield_interval;
rswindell's avatar
rswindell committed
908
	js_branch.terminated = &terminated;
909
	js_branch.auto_terminate = TRUE;
910
911
912
913
914
915
916
917
918

	bool success=false;

	do {

		JS_SetErrorReporter(js_cx, js_ErrorReporter);

		JS_SetContextPrivate(js_cx, this);	/* Store a pointer to sbbs_t instance */

919
		/* Global Objects (including system, js, client, Socket, MsgBase, File, User, etc. */
920
		if((js_glob=js_CreateCommonObjects(js_cx, &scfg, &cfg, js_global_functions
921
922
923
					,uptime, startup->host_name, SOCKLIB_DESC	/* system */
					,&js_branch									/* js */
					,&client, client_socket						/* client */
924
					,&js_server_props							/* server */
925
			))==NULL)
926
927
928
929
930
931
932
933
934
935
			break;

		/* BBS Object */
		if(js_CreateBbsObject(js_cx, js_glob)==NULL)
			break;

		/* Console Object */
		if(js_CreateConsoleObject(js_cx, js_glob)==NULL)
			break;

936
937
938
939
940
941
942
943
944
945
946
947
948
949
		if(startup->js.thread_stack) {
			ulong stack_limit;

#if JS_STACK_GROWTH_DIRECTION > 0
			stack_limit=((ulong)stack_frame)+startup->js.thread_stack;
#else
			stack_limit=((ulong)stack_frame)-startup->js.thread_stack;
#endif
			JS_SetThreadStackLimit(js_cx, stack_limit);

			lprintf(LOG_DEBUG,"%s JavaScript: Thread stack limit: %lu bytes"
				,node, startup->js.thread_stack);
		}

950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
		success=true;

	} while(0);

	if(!success) {
		JS_DestroyContext(js_cx);
		js_cx=NULL;
		return(false);
	}

	return(true);
}

void sbbs_t::js_create_user_objects(void)
{
	if(js_cx==NULL)
		return;

	if(!js_CreateUserObjects(js_cx, js_glob, &cfg, &useron, NULL, subscan)) 
969
		lprintf(LOG_ERR,"!JavaScript ERROR creating user objects");
970
971
972
973
974
975
976
977
978
979
980
}

#endif	/* JAVASCRIPT */

static BYTE* telnet_interpret(sbbs_t* sbbs, BYTE* inbuf, int inlen,
  									BYTE* outbuf, int& outlen)
{
	BYTE*   first_iac=NULL;
	BYTE*	first_cr=NULL;
	int 	i;

981
982
983
984
985
	if(inlen<1) {
		outlen=0;
		return(inbuf);	// no length? No interpretation
	}

986
987
    first_iac=(BYTE*)memchr(inbuf, TELNET_IAC, inlen);

988
989
	if(!(sbbs->telnet_mode&TELNET_MODE_GATE) 
		&& sbbs->telnet_remote_option[TELNET_BINARY_TX]!=TELNET_WILL
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
		&& !(sbbs->console&CON_RAW_IN)) {
		if(sbbs->telnet_last_rxch==CR)
			first_cr=inbuf;
		else
			first_cr=(BYTE*)memchr(inbuf, CR, inlen);
	}

    if(!sbbs->telnet_cmdlen	&& first_iac==NULL && first_cr==NULL) {
        outlen=inlen;
        return(inbuf);	// no interpretation needed
    }

    if(first_iac!=NULL || first_cr!=NULL) {
		if(first_iac!=NULL && (first_cr==NULL || first_iac<first_cr))
   			outlen=first_iac-inbuf;
		else
			outlen=first_cr-inbuf;
	    memcpy(outbuf, inbuf, outlen);
    } else
    	outlen=0;

    for(i=outlen;i<inlen;i++) {
1012
1013
		if(!(sbbs->telnet_mode&TELNET_MODE_GATE) 
			&& sbbs->telnet_remote_option[TELNET_BINARY_TX]!=TELNET_WILL
1014
1015
1016
1017
			&& !(sbbs->console&CON_RAW_IN)) {
			if(sbbs->telnet_last_rxch==CR
				&& (inbuf[i]==LF || inbuf[i]==0)) { // CR/LF or CR/NUL, ignore 2nd char
#if 0 /* Debug CR/LF problems */
rswindell's avatar
rswindell committed
1018
				lprintf(LOG_INFO,"Node %d CR/%02Xh detected and ignored"
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
					,sbbs->cfg.node_num, inbuf[i]);
#endif
				sbbs->telnet_last_rxch=inbuf[i];
				continue;
			}
			sbbs->telnet_last_rxch=inbuf[i];
		}

        if(inbuf[i]==TELNET_IAC && sbbs->telnet_cmdlen==1) { /* escaped 255 */
            sbbs->telnet_cmdlen=0;
            outbuf[outlen++]=TELNET_IAC;
            continue;
        }
        if(inbuf[i]==TELNET_IAC || sbbs->telnet_cmdlen) {
1033

1034
1035
			if(sbbs->telnet_cmdlen<sizeof(sbbs->telnet_cmd))
				sbbs->telnet_cmd[sbbs->telnet_cmdlen++]=inbuf[i];
1036
1037
1038
1039
1040

			uchar command	= sbbs->telnet_cmd[1];
			uchar option	= sbbs->telnet_cmd[2];

			if(sbbs->telnet_cmdlen>=2 && command==TELNET_SB) {
1041
1042
1043
				if(inbuf[i]==TELNET_SE 
					&& sbbs->telnet_cmd[sbbs->telnet_cmdlen-2]==TELNET_IAC) {
					/* sub-option terminated */
1044
					if(option==TELNET_TERM_TYPE
1045
						&& sbbs->telnet_cmd[3]==TELNET_TERM_IS) {
1046
						sprintf(sbbs->terminal,"%.*s",(int)sbbs->telnet_cmdlen-6,sbbs->telnet_cmd+4);
rswindell's avatar
rswindell committed
1047
						lprintf(LOG_DEBUG,"Node %d %s telnet terminal type: %s"
1048
1049
1050
	                		,sbbs->cfg.node_num
							,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
							,sbbs->terminal);
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066

					} else if(option==TELNET_TERM_SPEED
						&& sbbs->telnet_cmd[3]==TELNET_TERM_IS) {
						char speed[128];
						sprintf(speed,"%.*s",(int)sbbs->telnet_cmdlen-6,sbbs->telnet_cmd+4);
						lprintf(LOG_DEBUG,"Node %d %s telnet terminal speed: %s"
	                		,sbbs->cfg.node_num
							,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
							,speed);

					} else if(option==TELNET_NEGOTIATE_WINDOW_SIZE) {
						long cols = (sbbs->telnet_cmd[3]<<8) | sbbs->telnet_cmd[4];
						long rows = (sbbs->telnet_cmd[5]<<8) | sbbs->telnet_cmd[6];
						lprintf(LOG_DEBUG,"Node %d %s telnet window size: %ux%u"
	                		,sbbs->cfg.node_num
							,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
1067
1068
							,cols
							,rows);
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
						if(rows && !sbbs->useron.rows)	/* auto-detect rows */
							sbbs->rows=rows;
						if(cols)
							sbbs->cols=cols;

					} else if(startup->options&BBS_OPT_DEBUG_TELNET)
            			lprintf(LOG_DEBUG,"Node %d %s unsupported telnet sub-negotiation cmd: %s"
	                		,sbbs->cfg.node_num
							,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
                			,telnet_opt_desc(option));
1079
1080
1081
1082
					sbbs->telnet_cmdlen=0;
				}
			}
            else if(sbbs->telnet_cmdlen==2 && inbuf[i]<TELNET_WILL) {
1083
				if(startup->options&BBS_OPT_DEBUG_TELNET)
rswindell's avatar
rswindell committed
1084
            		lprintf(LOG_DEBUG,"Node %d %s telnet cmd: %s"
1085
1086
	                	,sbbs->cfg.node_num
						,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
1087
                		,telnet_cmd_desc(option));
1088
1089
                sbbs->telnet_cmdlen=0;
            }
1090
1091
            else if(sbbs->telnet_cmdlen>=3) {	/* telnet option negotiation */

1092
1093
1094
1095
1096
1097
				if(startup->options&BBS_OPT_DEBUG_TELNET)
					lprintf(LOG_DEBUG,"Node %d %s telnet cmd: %s %s"
						,sbbs->cfg.node_num
						,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
						,telnet_cmd_desc(command)
						,telnet_opt_desc(option));
1098

1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
				if(!(sbbs->telnet_mode&TELNET_MODE_GATE)) {
					if(command==TELNET_DO || command==TELNET_DONT) {	/* local options */
						if(sbbs->telnet_local_option[option]!=command) {
							sbbs->telnet_local_option[option]=command;
							sbbs->send_telnet_cmd(telnet_opt_ack(command),option);
						}
					} else { /* WILL/WONT (remote options) */ 
						if(sbbs->telnet_remote_option[option]!=command) {	
						
							switch(option) {
								case TELNET_BINARY_TX:
								case TELNET_ECHO:
								case TELNET_TERM_TYPE:
								case TELNET_TERM_SPEED:
								case TELNET_SUP_GA:
								case TELNET_NEGOTIATE_WINDOW_SIZE:
									sbbs->telnet_remote_option[option]=command;
									sbbs->send_telnet_cmd(telnet_opt_ack(command),option);
									break;
								default: /* unsupported remote options */
									if(command==TELNET_WILL) /* NAK */
										sbbs->send_telnet_cmd(telnet_opt_nak(command),option);
									break;
							}
1123
						}
1124

1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
						if(command==TELNET_WILL && option==TELNET_TERM_TYPE) {
							if(startup->options&BBS_OPT_DEBUG_TELNET)
								lprintf(LOG_DEBUG,"Node %d requesting telnet terminal type"
									,sbbs->cfg.node_num);

							char	buf[64];
							sprintf(buf,"%c%c%c%c%c%c"
								,TELNET_IAC,TELNET_SB
								,TELNET_TERM_TYPE,TELNET_TERM_SEND
								,TELNET_IAC,TELNET_SE);
							sbbs->putcom(buf,6);
						}
						else if(command==TELNET_WILL && option==TELNET_TERM_SPEED) {
							if(startup->options&BBS_OPT_DEBUG_TELNET)
								lprintf(LOG_DEBUG,"Node %d requesting telnet terminal speed"
									,sbbs->cfg.node_num);

							char	buf[64];
							sprintf(buf,"%c%c%c%c%c%c"
								,TELNET_IAC,TELNET_SB
								,TELNET_TERM_SPEED,TELNET_TERM_SEND
								,TELNET_IAC,TELNET_SE);
							sbbs->putcom(buf,6);
						}
1149
					}
1150
				}
1151

1152
                sbbs->telnet_cmdlen=0;
1153

1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
            }
			if(sbbs->telnet_mode&TELNET_MODE_GATE)	// Pass-through commads
				outbuf[outlen++]=inbuf[i];
        } else
        	outbuf[outlen++]=inbuf[i];
    }
    return(outbuf);
}

void sbbs_t::send_telnet_cmd(uchar cmd, uchar opt)
{
	char buf[16];
1166
	
1167
1168
	if(telnet_mode&TELNET_MODE_OFF)	
		return;
1169
1170
1171

	if(cmd<TELNET_WILL) {
		if(startup->options&BBS_OPT_DEBUG_TELNET)
rswindell's avatar
rswindell committed
1172
            lprintf(LOG_DEBUG,"Node %d sending telnet cmd: %s"
1173
1174
1175
1176
1177
1178
	            ,cfg.node_num
                ,telnet_cmd_desc(cmd));
		sprintf(buf,"%c%c",TELNET_IAC,cmd);
		putcom(buf,2);
	} else {
		if(startup->options&BBS_OPT_DEBUG_TELNET)
rswindell's avatar
rswindell committed
1179
			lprintf(LOG_DEBUG,"Node %d sending telnet cmd: %s %s"
1180
1181
1182
1183
1184
1185
1186
1187
				,cfg.node_num
				,telnet_cmd_desc(cmd)
				,telnet_opt_desc(opt));
		sprintf(buf,"%c%c%c",TELNET_IAC,cmd,opt);
		putcom(buf,3);
	}
}

1188
1189
void sbbs_t::request_telnet_opt(uchar cmd, uchar opt)
{
1190
1191
1192
1193
1194
1195
1196
1197
1198
	if(cmd==TELNET_DO || cmd==TELNET_DONT) {	/* remote option */
		if(telnet_remote_option[opt]==telnet_opt_ack(cmd))
			return;	/* already set in this mode, do nothing */
		telnet_remote_option[opt]=telnet_opt_ack(cmd);
	} else {	/* local option */
		if(telnet_local_option[opt]==telnet_opt_ack(cmd))
			return;	/* already set in this mode, do nothing */
		telnet_local_option[opt]=telnet_opt_ack(cmd);
	}
1199
1200
1201
	send_telnet_cmd(cmd,opt);
}

1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
void input_thread(void *arg)
{
	BYTE		inbuf[4000];
   	BYTE		telbuf[sizeof(inbuf)];
    BYTE		*wrbuf;
    int			i,rd,wr,avail;
	ulong		total_recv=0;
	ulong		total_pkts=0;
	fd_set		socket_set;
	sbbs_t*		sbbs = (sbbs_t*) arg;
	struct timeval	tv;
1213
1214
	SOCKET		high_socket;
	SOCKET		sock;
1215
1216
1217
1218

	thread_up(TRUE /* setuid */);

#ifdef _DEBUG
rswindell's avatar
rswindell committed
1219
	lprintf(LOG_DEBUG,"Node %d input thread started",sbbs->cfg.node_num);
1220
1221
1222
1223
1224
1225
1226
1227
1228
#endif

	pthread_mutex_init(&sbbs->input_thread_mutex,NULL);
    sbbs->input_thread_running = true;
	sbbs->console|=CON_R_INPUT;

	while(sbbs->online && sbbs->client_socket!=INVALID_SOCKET
		&& node_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) {

rswindell's avatar
rswindell committed
1229
1230
		if(pthread_mutex_lock(&sbbs->input_thread_mutex)!=0)
			sbbs->errormsg(WHERE,ERR_LOCK,"input_thread_mutex",0);
1231

1232
1233
1234
		FD_ZERO(&socket_set);
		FD_SET(sbbs->client_socket,&socket_set);
		high_socket=sbbs->client_socket;
1235
1236
#ifdef __unix__
		if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET)  {
1237
1238
1239
			FD_SET(uspy_socket[sbbs->cfg.node_num-1],&socket_set);
			if(uspy_socket[sbbs->cfg.node_num-1] > high_socket)
				high_socket=uspy_socket[sbbs->cfg.node_num-1];
1240
1241
1242
		}
#endif

1243
1244
		tv.tv_sec=1;
		tv.tv_usec=0;
1245

1246
		if((i=select(high_socket+1,&socket_set,NULL,NULL,&tv))<1) {
rswindell's avatar
rswindell committed
1247
1248
			if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
				sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1249
1250
1251
1252
			if(i==0) {
				YIELD();	/* This kludge is necessary on some Linux distros */
				continue;	/* to allow other threads to lock the input_thread_mutex */
			}
1253
1254
1255

			if(sbbs->client_socket==INVALID_SOCKET)
				break;
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
#ifdef __unix__
			if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) {
				if(!socket_check(uspy_socket[sbbs->cfg.node_num-1],NULL,NULL,0)) {
					close_socket(uspy_socket[sbbs->cfg.node_num-1]);
					lprintf(LOG_NOTICE,"Closing local spy socket: %d",uspy_socket[sbbs->cfg.node_num-1]);
					uspy_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
					continue;
				}
			}
#endif
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
	       	if(ERROR_VALUE == ENOTSOCK)
    	        lprintf(LOG_NOTICE,"Node %d socket closed by peer on input->select", sbbs->cfg.node_num);
			else if(ERROR_VALUE==ESHUTDOWN)
				lprintf(LOG_NOTICE,"Node %d socket shutdown on input->select", sbbs->cfg.node_num);
			else if(ERROR_VALUE==EINTR)
				lprintf(LOG_NOTICE,"Node %d input thread interrupted",sbbs->cfg.node_num);
            else if(ERROR_VALUE==ECONNRESET) 
				lprintf(LOG_NOTICE,"Node %d connection reset by peer on input->select", sbbs->cfg.node_num);
	        else if(ERROR_VALUE==ECONNABORTED) 
				lprintf(LOG_NOTICE,"Node %d connection aborted by peer on input->select", sbbs->cfg.node_num);
			else
				lprintf(LOG_WARNING,"Node %d !ERROR %d input->select socket %d"
               		,sbbs->cfg.node_num, ERROR_VALUE, sbbs->client_socket);
			break;
1280
1281
		}

1282
1283
1284
1285
1286
1287
		if(sbbs->client_socket==INVALID_SOCKET) {
			if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
				sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
			break;
		}

1288
1289
1290
/*         ^          ^
 *		\______    ______/
 *       \  * \   / *   /
deuce's avatar
deuce committed
1291
1292
1293
 *        -----   ------           /----\
 *              ||               -< Boo! |
 *             /_\                 \----/
1294
1295
1296
 *       \______________/
 *        \/\/\/\/\/\/\/
 *         ------------
deuce's avatar
deuce committed
1297
1298
 */

1299
1300
1301
		if(FD_ISSET(sbbs->client_socket,&socket_set))
			sock=sbbs->client_socket;
#ifdef __unix__
1302
1303
		else if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET
				&& FD_ISSET(uspy_socket[sbbs->cfg.node_num-1],&socket_set))  {
deuce's avatar
deuce committed
1304
1305
			if(!socket_check(uspy_socket[sbbs->cfg.node_num-1],NULL,NULL,0)) {
				close_socket(uspy_socket[sbbs->cfg.node_num-1]);
deuce's avatar
deuce committed
1306
				lprintf(LOG_NOTICE,"Closing local spy socket: %d",uspy_socket[sbbs->cfg.node_num-1]);
deuce's avatar
deuce committed
1307
				uspy_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
1308
1309
				if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
					sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
deuce's avatar
deuce committed
1310
1311
				continue;
			}
1312
			sock=uspy_socket[sbbs->cfg.node_num-1];
1313
		}
1314
#endif
1315
1316
1317
		else {
			if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
				sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1318
			continue;
1319
		}
1320

1321
1322
1323
    	rd=RingBufFree(&sbbs->inbuf);

		if(!rd) { // input buffer full
1324
			lprintf(LOG_WARNING,"Node %d !WARNING input buffer full", sbbs->cfg.node_num);
1325
1326
1327
1328
1329
        	// wait up to 5 seconds to empty (1 byte min)
			time_t start=time(NULL);
            while((rd=RingBufFree(&sbbs->inbuf))==0) {
            	if(time(NULL)-start>=5) {
                	rd=1;
1330
1331
					if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
						sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
                	break;
                }
                YIELD();
            }
		}
		
	    if(rd > (int)sizeof(inbuf))
        	rd=sizeof(inbuf);

    	rd = recv(sock, (char*)inbuf, rd, 0);

rswindell's avatar
rswindell committed
1343
1344
		if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
			sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1345
1346
1347

		if(rd == SOCKET_ERROR)
		{
1348
#ifdef __unix__
1349
			if(sock==sbbs->client_socket)  {
1350
#endif
1351
	        	if(ERROR_VALUE == ENOTSOCK)
rswindell's avatar
rswindell committed
1352
    	            lprintf(LOG_NOTICE,"Node %d socket closed by peer on receive", sbbs->cfg.node_num);
1353
        	    else if(ERROR_VALUE==ECONNRESET) 
rswindell's avatar
rswindell committed
1354
					lprintf(LOG_NOTICE,"Node %d connection reset by peer on receive", sbbs->cfg.node_num);
1355
				else if(ERROR_VALUE==ESHUTDOWN)
rswindell's avatar
rswindell committed
1356
					lprintf(LOG_NOTICE,"Node %d socket shutdown on receive", sbbs->cfg.node_num);
1357
        	    else if(ERROR_VALUE==ECONNABORTED) 
rswindell's avatar
rswindell committed
1358
					lprintf(LOG_NOTICE,"Node %d connection aborted by peer on receive", sbbs->cfg.node_num);
1359
				else
rswindell's avatar
rswindell committed
1360
					lprintf(LOG_WARNING,"Node %d !ERROR %d receiving from socket %d"
1361
1362
1363
        	        	,sbbs->cfg.node_num, ERROR_VALUE, sock);
				break;
#ifdef __unix__
1364
			} else  {
1365
				if(ERROR_VALUE != EAGAIN)  {
rswindell's avatar
rswindell committed
1366
					lprintf(LOG_ERR,"Node %d !ERROR %d on local spy socket %d receive"
1367
						, sbbs->cfg.node_num, errno, sock);
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
					close_socket(uspy_socket[sbbs->cfg.node_num-1]);
					uspy_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
				}
				continue;
			}
#endif
		}

		if(rd == 0 && sock==sbbs->client_socket)
		{
rswindell's avatar