main.cpp 152 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 2008 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
 *																			*
 * 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" 
41
#include "netwrap.h"
42
#include "js_rtpool.h"
43
#include "js_request.h"
44
45
46

#ifdef __unix__
	#include <sys/un.h>
deuce's avatar
deuce committed
47
48
49
50
	#ifndef SUN_LEN
		#define SUN_LEN(su) \
	        (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
	#endif
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

73
#ifdef USE_CRYPTLIB
rswindell's avatar
rswindell committed
74
	#define SSH_END()	if(ssh)	cryptDestroySession(sbbs->ssh_session);
75
76
77
78
#else
	#define	SSH_END()
#endif

79
80
81
time_t	uptime=0;
DWORD	served=0;

deuce's avatar
deuce committed
82
static	DWORD node_threads_running=0;
83
static	ulong thread_count=0;
84
85
86
87
88
89
90
91
92
93
		
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;
94
95
96
#ifdef USE_CRYPTLIB
static	SOCKET ssh_socket=INVALID_SOCKET;
#endif
97
98
99
100
101
static	sbbs_t*	sbbs=NULL;
static	scfg_t	scfg;
static	char *	text[TOTAL_TEXT];
static	WORD	first_node;
static	WORD	last_node;
102
static	bool	terminate_server=false;
103
104
static	str_list_t recycle_semfiles;
static	str_list_t shutdown_semfiles;
105
106
107
#ifdef _THREAD_SUID_BROKEN
int	thread_suid_broken=TRUE;			/* NPTL is no longer broken */
#endif
108
109
110
111
112

extern "C" {

static bbs_startup_t* startup=NULL;

113
static void status(const char* str)
114
115
{
	if(startup!=NULL && startup->status!=NULL)
116
	    startup->status(startup->cbdata,str);
117
118
119
120
121
}

static void update_clients()
{
	if(startup!=NULL && startup->clients!=NULL)
122
		startup->clients(startup->cbdata,node_threads_running);
123
124
125
126
127
}

void client_on(SOCKET sock, client_t* client, BOOL update)
{
	if(startup!=NULL && startup->client_on!=NULL)
128
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
129
130
131
132
133
}

static void client_off(SOCKET sock)
{
	if(startup!=NULL && startup->client_on!=NULL)
134
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
135
136
137
138
139
140
}

static void thread_up(BOOL setuid)
{
	thread_count++;
	if(startup!=NULL && startup->thread_up!=NULL)
141
		startup->thread_up(startup->cbdata,TRUE,setuid);
142
143
144
145
146
147
148
}

static void thread_down()
{
	if(thread_count>0)
		thread_count--;
	if(startup!=NULL && startup->thread_up!=NULL)
149
		startup->thread_up(startup->cbdata,FALSE,FALSE);
150
151
}

152
int lputs(int level, const char* str)
153
{
rswindell's avatar
rswindell committed
154
	if(startup==NULL || startup->lputs==NULL || str==NULL)
155
156
    	return(0);

157
    return(startup->lputs(startup->cbdata,level,str));
158
159
}

160
int lprintf(int level, const char *fmt, ...)
161
162
163
164
165
166
167
168
{
	va_list argptr;
	char sbuf[1024];

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
169
    return(lputs(level,sbuf));
170
171
}

172
int eprintf(int level, const char *fmt, ...)
173
174
175
176
{
	va_list argptr;
	char sbuf[1024];

177
    if(startup==NULL || startup->event_lputs==NULL)
178
179
180
181
182
183
184
        return(0);

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

188
SOCKET open_socket(int type, const char* protocol)
189
190
191
192
193
194
{
	SOCKET	sock;
	char	error[256];

	sock=socket(AF_INET, type, IPPROTO_IP);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
195
		startup->socket_open(startup->cbdata,TRUE);
196
	if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
197
		lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
198
199
200
201
202
203
204
205
206
207

	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) 
208
		startup->socket_open(startup->cbdata,TRUE);
209
210
211
212
213
214
215
216
217
218
219
220
221

	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);
222
	if(startup!=NULL && startup->socket_open!=NULL)
223
		startup->socket_open(startup->cbdata,FALSE);
224
	if(result!=0 && ERROR_VALUE!=ENOTSOCK)
225
		lprintf(LOG_ERR,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
226
227
228
229
230
231
232
233
234
235
	return(result);
}


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

	if(*addr==0)
236
		return((u_long)INADDR_NONE);
237
238
239
240
241
242
243

	for(p=addr;*p;p++)
		if(*p!='.' && !isdigit(*p))
			break;
	if(!(*p))
		return(inet_addr(addr));
	if((host=gethostbyname(addr))==NULL) 
244
		return((u_long)INADDR_NONE);
245
246
247
248
249
	return(*((ulong*)host->h_addr_list[0]));
}

} /* extern "C" */

250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#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

277
278
DLLEXPORT void DLLCALL sbbs_srand()
{
279
280
281
282
283
284
285
286
287
288
289
290
	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);
291
292
293
	sbbs_random(10);	/* Throw away first number */
}

294
int DLLCALL sbbs_random(int n)
295
296
297
298
{
	return(xp_random(n));
}

299
300
#ifdef JAVASCRIPT

301
302
static js_server_props_t js_server_props;

303
304
305
306
307
308
309
310
311
312
313
314
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
315
316
		if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)	/* Assertion here, in _heap_alloc_dbg, June-21-2004 */
			return(JS_FALSE);								/* Caused by nntpservice.js? */
317

318
319
320
321
	if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
		,NULL,NULL,flags))
		return(JS_FALSE);

322
323
324
325
326
327
328
329
330
331
332
	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;
	}

333
	return(JS_TRUE);
334
335
}

336
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
337
338

JSBool
339
DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
340
341
342
343
344
345
{
	JSString* js_str = JS_NewStringCopyZ(cx, str);

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

rswindell's avatar
rswindell committed
346
347
348
	if(ver < 10000)		/* auto convert 313 to 31300 */
		ver*=100;

349
	return(JS_DefineProperty(cx,obj,"_description"
350
351
352
			,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
		&& JS_DefineProperty(cx,obj,"_ver"
			,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
353
354
355
}

JSBool
356
DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
357
358
359
360
361
362
363
364
365
366
{
	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));
}

367
#ifdef BUILD_JSDOCS
368
369

static const char* method_array_name = "_method_list";
370
static const char* propver_array_name = "_property_ver_list";
371
372
373
374
375
376
377
378
379
380
381
382
383

/*
 * 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",
384
385
	"array",
	"alias",
rswindell's avatar
rswindell committed
386
387
	"undefined",
	"null"
388
389
};

390
391
392
393
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint		i;
394
	long		ver;
395
396
397
398
399
400
401
402
403
404
405
406
	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++) {
407
408
		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))
409
410
			return(JS_FALSE);
		if(props[i].flags&JSPROP_ENUMERATE) {	/* No need to version invisible props */
411
412
413
			if((ver=props[i].ver) < 10000)		/* auto convert 313 to 31300 */
				ver*=100;
			val = INT_TO_JSVAL(ver);
414
415
416
417
418
419
420
421
			if(!JS_SetElement(cx, array, len++, &val))
				return(JS_FALSE);
		}
	}

	return(JS_TRUE);
}

422
JSBool 
423
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
424
425
426
{
	int			i;
	jsuint		len=0;
427
	long		ver;
428
429
430
431
432
433
434
435
436
437
438
439
	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);

440
441
442
443
	if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
		, NULL, NULL, 0))
		return(JS_FALSE);

444
445
446
447
448
449
	if(append)
		if(!JS_GetArrayLength(cx, method_array, &len))
			return(JS_FALSE);

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

450
451
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
452
453
454
455

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

456
		method = JS_NewObject(cx, NULL, NULL, method_array);	/* exception here June-7-2003 */
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490

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

491
		if(funcs[i].ver) {
492
			if((ver=funcs[i].ver) < 10000)		/* auto convert 313 to 31300 */
493
494
				ver*=100;
			val = INT_TO_JSVAL(ver);
495
496
497
			JS_SetProperty(cx,method, "ver", &val);
		}

498
499
500
501
502
503
504
505
		val=OBJECT_TO_JSVAL(method);
		if(!JS_SetElement(cx, method_array, len+i, &val))
			return(JS_FALSE);
	}

	return(JS_TRUE);
}

506
507
508
509
510
/*
 * Always resolve all here since
 * 1) We'll always be enumerating anyways
 * 2) The speed penalty won't be seen in production code anyways
 */
511
512
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
513
514
515
{
	JSBool	ret=JS_TRUE;

deuce's avatar
deuce committed
516
	if(props) {
517
518
		if(!js_DefineSyncProperties(cx, obj, props))
			ret=JS_FALSE;
deuce's avatar
deuce committed
519
	}
520
		
deuce's avatar
deuce committed
521
522
	if(funcs) {
		if(!js_DefineSyncMethods(cx, obj, funcs, 0))
523
			ret=JS_FALSE;
deuce's avatar
deuce committed
524
	}
525

deuce's avatar
deuce committed
526
527
528
529
	if(consts) {
		if(!js_DefineConstIntegers(cx, obj, consts, flags))
			ret=JS_FALSE;
	}
530

531
532
533
	return(ret);
}

534
#else // NON-JSDOCS
535

536
537
538
539
540
541
542
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
	uint i;

	for(i=0;props[i].name;i++) 
		if(!JS_DefinePropertyWithTinyId(cx, obj, 
543
			props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
544
545
546
547
548
549
			return(JS_FALSE);

	return(JS_TRUE);
}


550
JSBool 
551
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
552
{
553
	uint i;
554
555

	for(i=0;funcs[i].name;i++)
556
557
		if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
			return(JS_FALSE);
558
559
560
	return(JS_TRUE);
}

561
562
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
563
564
{
	uint i;
565
	jsval	val;
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587

	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);
			}
		}
	}
588
	if(consts) {
deuce's avatar
deuce committed
589
		for(i=0;consts[i].name;i++) {
590
			if(name==NULL || strcmp(name, consts[i].name)==0) {
deuce's avatar
deuce committed
591
	        	if(!JS_NewNumberValue(cx, consts[i].val, &val))
592
593
					return(JS_FALSE);

deuce's avatar
deuce committed
594
				if(!JS_DefineProperty(cx, obj, consts[i].name, val ,NULL, NULL, flags))
595
596
597
598
599
600
601
					return(JS_FALSE);

				if(name)
					return(JS_TRUE);
			}
		}
	}
602
603
604
605

	return(JS_TRUE);
}

606
607
#endif

608
609
610
611
612
613
614
615
616
617
618
619
620
621
/* 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);
	}
622

623
624
625
	return(JS_TRUE);
}

626
627
628
629
630
631
632
633
634
635
636
637
638
639
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));
}

640
641
642
static JSBool
js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
643
644
    uintN		i=0;
	int32		level=LOG_INFO;
645
    JSString*	str=NULL;
646
	sbbs_t*		sbbs;
647
	jsrefcount	rc;
648
649
650
651

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

652
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i]))
653
654
655
		JS_ValueToInt32(cx,argv[i++],&level);

    for(; i<argc; i++) {
656
		if((str=JS_ValueToString(cx, argv[i]))==NULL) {
657
			JS_RESUMEREQUEST(cx, rc);
658
		    return(JS_FALSE);
659
		}
660
		rc=JS_SUSPENDREQUEST(cx);
661
		if(sbbs->online==ON_LOCAL) {
662
			if(startup!=NULL && startup->event_lputs!=NULL)
663
				startup->event_lputs(level,JS_GetStringBytes(str));
664
		} else
665
			lputs(level,JS_GetStringBytes(str));
666
		JS_RESUMEREQUEST(cx, rc);
667
	}
668

669
670
671
672
	if(str==NULL)
		*rval = JSVAL_VOID;
	else
		*rval = STRING_TO_JSVAL(str);
673
674
675
    return(JS_TRUE);
}

676
677
678
679
680
681
static JSBool
js_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	uchar*		buf;
	int32		len=128;
	sbbs_t*		sbbs;
682
	jsrefcount	rc;
683
684
685
686
687
688
689
690
691
692

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

693
	rc=JS_SUSPENDREQUEST(cx);
694
	len=RingBufRead(&sbbs->inbuf,buf,len);
695
	JS_RESUMEREQUEST(cx, rc);
696
697
698
699
700
701
702
703
704
705
706
707
708
709

	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;
710
	jsrefcount	rc;
711
712
713
714
715
716
717
718
719
720

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

721
	rc=JS_SUSPENDREQUEST(cx);
722
	len=sbbs->getstr(buf,len,K_NONE);
723
	JS_RESUMEREQUEST(cx, rc);
724
725
726
727
728
729
730
731

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

	free(buf);
	return(JS_TRUE);
}

732
static JSBool
733
js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
734
735
{
    uintN		i;
736
    JSString*	str=NULL;
737
	sbbs_t*		sbbs;
738
	jsrefcount	rc;
739
740
741
742
743
744
745

	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);
746
		rc=JS_SUSPENDREQUEST(cx);
747
		if(sbbs->online==ON_LOCAL)
748
			eprintf(LOG_INFO,"%s",JS_GetStringBytes(str));
749
750
		else
			sbbs->bputs(JS_GetStringBytes(str));
751
		JS_RESUMEREQUEST(cx, rc);
752
753
754
755
756
757
758
759
760
	}

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

761
762
763
764
765
766
767
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;
768
	jsrefcount	rc;
769
770
771
772
773
774
775

	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);
776
		rc=JS_SUSPENDREQUEST(cx);
777
		sbbs->putcom(str, len);
778
		JS_RESUMEREQUEST(cx, rc);
779
780
781
782
783
	}

    return(JS_TRUE);
}

784
static JSBool
785
js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
786
787
{
	sbbs_t*		sbbs;
788
	jsrefcount	rc;
789
790
791
792
793

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

	js_write(cx,obj,argc,argv,rval);
794
	rc=JS_SUSPENDREQUEST(cx);
795
796
	if(sbbs->online==ON_REMOTE)
		sbbs->bputs(crlf);
797
	JS_RESUMEREQUEST(cx, rc);
798
799
800
801
802
803
804
805
806

    return(JS_TRUE);
}

static JSBool
js_printf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	char*		p;
	sbbs_t*		sbbs;
807
	jsrefcount	rc;
808
809
810
811

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

812
813
	if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
		JS_ReportError(cx,"js_sprintf failed");
814
815
816
		return(JS_FALSE);
	}

817
	rc=JS_SUSPENDREQUEST(cx);
818
	if(sbbs->online==ON_LOCAL)
819
		eprintf(LOG_INFO,"%s",p);
820
821
	else
		sbbs->bputs(p);
822
	JS_RESUMEREQUEST(cx, rc);
823
824
825

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

826
	js_sprintf_free(p);
827
828
829
830
831
832
833
834
835

    return(JS_TRUE);
}

static JSBool
js_alert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *	str;
	sbbs_t*		sbbs;
836
	jsrefcount	rc;
837
838
839
840
841
842
843

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

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

844
	rc=JS_SUSPENDREQUEST(cx);
845
846
847
848
	sbbs->attr(sbbs->cfg.color[clr_err]);
	sbbs->bputs(JS_GetStringBytes(str));
	sbbs->attr(LIGHTGRAY);
	sbbs->bputs(crlf);
849
	JS_RESUMEREQUEST(cx, rc);
850
851
852
853
854
855
856
857
858

    return(JS_TRUE);
}

static JSBool
js_confirm(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    JSString *	str;
	sbbs_t*		sbbs;
859
	jsrefcount	rc;
860
861
862
863
864
865
866

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

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

867
	rc=JS_SUSPENDREQUEST(cx);
868
	*rval = BOOLEAN_TO_JSVAL(sbbs->yesno(JS_GetStringBytes(str)));
869
	JS_RESUMEREQUEST(cx, rc);
870
871
872
873
874
875
876
877
878
879
	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;
880
	jsrefcount	rc;
881
882
883
884
885
886
887
888
889
890
891
892
893
894

	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;

895
	rc=JS_SUSPENDREQUEST(cx);
896
897
898
899
	sbbs->bprintf("\1n\1y\1h%s\1w: ",JS_GetStringBytes(prompt));

	if(!sbbs->getstr(instr,sizeof(instr)-1,K_EDIT)) {
		*rval = JSVAL_NULL;
900
		JS_RESUMEREQUEST(cx, rc);
901
902
		return(JS_TRUE);
	}
903
	JS_RESUMEREQUEST(cx, rc);
904
905
906
907
908
909
910
911

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

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

912
static jsSyncMethodSpec js_global_functions[] = {
913
	{"log",				js_log,				1,	JSTYPE_STRING,	JSDOCSTR("[level,] value [,value]")
914
	,JSDOCSTR("add a line of text to the server and/or system log, "
915
916
		"<i>values</i> are typically string constants or variables, "
		"<i>level</i> is the debug level/priority (default: <tt>LOG_INFO</tt>)")
917
	,311
918
	},
919
920
	{"read",			js_read,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read up to count characters from input stream")
921
	,311
922
923
924
	},
	{"readln",			js_readln,			0,	JSTYPE_STRING,	JSDOCSTR("[count]")
	,JSDOCSTR("read a single line, up to count characters, from input stream")
925
	,311
926
	},
927
	{"write",			js_write,			0,	JSTYPE_VOID,	JSDOCSTR("value [,value]")
928
	,JSDOCSTR("send one or more values (typically strings) to the server output")
929
	,311
930
	},
931
932
	{"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")
rswindell's avatar
rswindell committed
933
	,314
934
	},
935
936
937
938
	{"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)")
939
	,311
940
	},
941
    {"printf",          js_printf,          1,	JSTYPE_STRING,	JSDOCSTR("string format [,value][,value]")
942
	,JSDOCSTR("print a formatted string - <small>CAUTION: for experienced C programmers ONLY</small>")
943
	,310
944
945
946
	},	
	{"alert",			js_alert,			1,	JSTYPE_VOID,	JSDOCSTR("value")
	,JSDOCSTR("print an alert message (ala client-side JS)")
947
	,310
948
949
950
	},
	{"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)")
951
	,310
952
953
954
955
	},
	{"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)")
956
	,310
957
958
959
960
961
962
963
964
965
966
967
	},
    {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;
968
	jsrefcount	rc;
969
970
971
972
973

	if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
		return;
	
	if(report==NULL) {
974
		lprintf(LOG_ERR,"!JavaScript: %s", message);
975
976
977
978
		return;
    }

	if(report->filename)
979
		SAFEPRINTF(file," %s",report->filename);
980
981
982
983
	else
		file[0]=0;

	if(report->lineno)
984
		SAFEPRINTF(line," line %d",report->lineno);
985
986
987
988
989
990
991
992
993
994
995
	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;

996
	rc=JS_SUSPENDREQUEST(cx);
997
	if(sbbs->online==ON_LOCAL) 
998
		eprintf(LOG_ERR,"!JavaScript %s%s%s: %s",warning,file,line,message);
999
	else {
1000
		lprintf(LOG_ERR,"!JavaScript %s%s%s: %s",warning,file,line,message);
1001
1002
		sbbs->bprintf("!JavaScript %s%s%s: %s\r\n",warning,file,line,message);
	}
1003
	JS_RESUMEREQUEST(cx, rc);
1004
1005
}

1006
bool sbbs_t::js_init(ulong* stack_frame)
1007
1008
1009
1010
{
	char		node[128];

    if(cfg.node_num)
1011
    	SAFEPRINTF(node,"Node %d",cfg.node_num);
1012
    else
1013
    	SAFECOPY(node,client_name);
1014

1015
1016
1017
	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;

1018
	lprintf(LOG_DEBUG,"%s JavaScript: Creating runtime: %lu bytes"
1019
		,node,startup->js.max_bytes);
1020

1021
	if((js_runtime = jsrt_GetNew(startup->js.max_bytes, 1000, __FILE__, __LINE__))==NULL)
1022
1023
		return(false);

1024
	lprintf(LOG_DEBUG,"%s JavaScript: Initializing context (stack: %lu bytes)"
1025
		,node,startup->js.cx_stack);
1026

1027
    if((js_cx = JS_NewContext(js_runtime, startup->js.cx_stack))==NULL)
1028
		return(false);
1029
	JS_BEGINREQUEST(js_cx);
rswindell's avatar
rswindell committed
1030
1031
	
	memset(&js_branch,0,sizeof(js_branch));
1032
1033
1034
	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
1035
	js_branch.terminated = &terminated;
1036
	js_branch.auto_terminate = TRUE;
1037
1038
1039
1040
1041
1042
1043
1044
1045

	bool success=false;

	do {

		JS_SetErrorReporter(js_cx, js_ErrorReporter);

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

1046
		/* Global Objects (including system, js, client, Socket, MsgBase, File, User, etc. */
1047
		if((js_glob=js_CreateCommonObjects(js_cx, &scfg, &cfg, js_global_functions
1048
1049
1050
					,uptime, startup->host_name, SOCKLIB_DESC	/* system */
					,&js_branch									/* js */
					,&client, client_socket						/* client */
1051
					,&js_server_props							/* server */
1052
			))==NULL)
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
			break;

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

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

1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
		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);
		}

1077
1078
1079
1080
		success=true;

	} while(0);

1081
	JS_ENDREQUEST(js_cx);
1082
1083
1084
1085
1086
1087
1088
1089
1090
	if(!success) {
		JS_DestroyContext(js_cx);
		js_cx=NULL;
		return(false);
	}

	return(true);
}

1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
void sbbs_t::js_cleanup(const char* node)
{
	/* Free Context */
	if(js_cx!=NULL) {	
		lprintf(LOG_DEBUG,"%s JavaScript: Destroying context",node);
		JS_DestroyContext(js_cx);
		js_cx=NULL;
	}

	if(js_runtime!=NULL) {
		lprintf(LOG_DEBUG,"%s JavaScript: Destroying runtime",node);
		jsrt_Release(js_runtime);
		js_runtime=NULL;
	}
}

1107
1108
1109
1110
1111
void sbbs_t::js_create_user_objects(void)
{
	if(js_cx==NULL)
		return;

1112
	JS_BEGINREQUEST(js_cx);
1113
	if(!js_CreateUserObjects(js_cx, js_glob, &cfg, &useron, NULL, subscan)) 
1114
		lprintf(LOG_ERR,"!JavaScript ERROR creating user objects");
1115
	JS_ENDREQUEST(js_cx);
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
}

#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;

1127
1128
1129
1130
1131
	if(inlen<1) {
		outlen=0;
		return(inbuf);	// no length? No interpretation
	}

1132
1133
    first_iac=(BYTE*)memchr(inbuf, TELNET_IAC, inlen);

1134
1135
	if(!(sbbs->telnet_mode&TELNET_MODE_GATE) 
		&& sbbs->telnet_remote_option[TELNET_BINARY_TX]!=TELNET_WILL
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
		&& !(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++) {
1158
1159
		if(!(sbbs->telnet_mode&TELNET_MODE_GATE) 
			&& sbbs->telnet_remote_option[TELNET_BINARY_TX]!=TELNET_WILL
1160
1161
1162
1163
			&& !(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
1164
				lprintf(LOG_INFO,"Node %d CR/%02Xh detected and ignored"
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
					,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) {
1179

1180
1181
			if(sbbs->telnet_cmdlen<sizeof(sbbs->telnet_cmd))
				sbbs->telnet_cmd[sbbs->telnet_cmdlen++]=inbuf[i];
1182
1183
1184
1185
1186

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

			if(sbbs->telnet_cmdlen>=2 && command==TELNET_SB) {
1187
1188
				if(inbuf[i]==TELNET_SE 
					&& sbbs->telnet_cmd[sbbs->telnet_cmdlen-2]==TELNET_IAC) {
1189
1190

					if(startup->options&BBS_OPT_DEBUG_TELNET)
1191
						lprintf(LOG_DEBUG,"Node %d %s telnet sub-negotiation command: %s"
1192
1193
1194
1195
	                		,sbbs->cfg.node_num
							,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
							,telnet_opt_desc(option));

1196
					/* sub-option terminated */
1197
					if(option==TELNET_TERM_TYPE
1198
						&& sbbs->telnet_cmd[3]==TELNET_TERM_IS) {
1199
						sprintf(sbbs->terminal,"%.*s",(int)sbbs->telnet_cmdlen-6,sbbs->telnet_cmd+4);
rswindell's avatar
rswindell committed
1200
						lprintf(LOG_DEBUG,"Node %d %s telnet terminal type: %s"
1201
1202
1203
	                		,sbbs->cfg.node_num
							,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
							,sbbs->terminal);
1204
1205
1206
1207
1208
1209
1210
1211
1212

					} 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);
1213
1214
						sbbs->cur_rate=atoi(speed);
						sbbs->cur_cps=sbbs->cur_rate/10;
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
#if 0
					} else if(option==TELNET_NEW_ENVIRON
						&& sbbs->telnet_cmd[3]==TELNET_ENVIRON_IS) {
						BYTE*	p;
						BYTE*   end=sbbs->telnet_cmd+(sbbs->telnet_cmdlen-2);
						for(p=sbbs->telnet_cmd+4; p < end; ) {
							if(*p==TELNET_ENVIRON_VAR || *p==TELNET_ENVIRON_USERVAR) {
								p++;
								lprintf(LOG_DEBUG,"Node %d %s telnet environment var/val: %.*s"
	                				,sbbs->cfg.node_num
									,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
									,end-p
									,p);
								p+=strlen((char*)p);
							} else
								p++;
						}
#endif