ftpsrvr.c 133 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/* ftpsrvr.c */

/* Synchronet FTP server */

/* $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 2004 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
 *																			*
 * 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.	*
 ****************************************************************************/

rswindell's avatar
rswindell committed
38
39
/* Platform-specific headers */
#ifdef _WIN32
40

41
	#include <share.h>		/* SH_DENYNO */
42
	#include <direct.h>		/* _mkdir/rmdir() */
43
44
45
	#include <process.h>	/* _beginthread */
	#include <windows.h>	/* required for mmsystem.h */
	#include <mmsystem.h>	/* SND_ASYNC */
46

rswindell's avatar
rswindell committed
47
48
#endif

49

rswindell's avatar
rswindell committed
50
/* ANSI C Library headers */
rswindell's avatar
rswindell committed
51
#include <stdio.h>
52
53
54
55
56
57
58
#include <stdlib.h>			/* ltoa in GNU C lib */
#include <stdarg.h>			/* va_list, varargs */
#include <string.h>			/* strrchr */
#include <fcntl.h>			/* O_WRONLY, O_RDONLY, etc. */
#include <errno.h>			/* EACCES */
#include <ctype.h>			/* toupper */
#include <sys/stat.h>		/* S_IWRITE */
rswindell's avatar
rswindell committed
59
60

/* Synchronet-specific headers */
61
#undef SBBS	/* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
62
#include "sbbs.h"
63
#include "text.h"			/* TOTAL_TEXT */
rswindell's avatar
rswindell committed
64
#include "ftpsrvr.h"
65
66
67
68
#include "telnet.h"

/* Constants */

69
#define FTP_SERVER				"Synchronet FTP Server"
70
71

#define STATUS_WFC				"Listening"
72
#define ANONYMOUS				"anonymous"
73
74
75
76
77

#define BBS_VIRTUAL_PATH		"bbs:/""/"	/* this is actually bbs:<slash><slash> */
#define LOCAL_FSYS_DIR			"local:"
#define BBS_FSYS_DIR			"bbs:"

78
#define TIMEOUT_THREAD_WAIT		60		/* Seconds */
79

80
81
#define TIMEOUT_SOCKET_LISTEN	30		/* Seconds */

82
83
84
85
86
87
#define XFER_REPORT_INTERVAL	60		/* Seconds */

#define INDEX_FNAME_LEN			15

#define	NAME_LEN				15		/* User name length for listings */

88
static ftp_startup_t*	startup=NULL;
89
90
91
92
static scfg_t	scfg;
static SOCKET	server_socket=INVALID_SOCKET;
static DWORD	active_clients=0;
static DWORD	sockets=0;
rswindell's avatar
rswindell committed
93
static DWORD	thread_count=0;
94
static time_t	uptime=0;
95
static DWORD	served=0;
96
static BOOL		terminate_server=FALSE;
97
static char		revision[16];
98
static char 	*text[TOTAL_TEXT];
99
100
101
static link_list_t recycle_semfiles;
static link_list_t shutdown_semfiles;

102
#ifdef _DEBUG
103
	static BYTE 	socket_debug[0x10000]={0};
104
105
106
107
108

	#define	SOCKET_DEBUG_CTRL		(1<<0)	// 0x01
	#define SOCKET_DEBUG_SEND		(1<<1)	// 0x02
	#define SOCKET_DEBUG_READLINE	(1<<2)	// 0x04
	#define SOCKET_DEBUG_ACCEPT		(1<<3)	// 0x08
rswindell's avatar
rswindell committed
109
	#define SOCKET_DEBUG_SENDTHREAD	(1<<4)	// 0x10
110
	#define SOCKET_DEBUG_TERMINATE	(1<<5)	// 0x20
111
	#define SOCKET_DEBUG_RECV_CHAR	(1<<6)	// 0x40
rswindell's avatar
rswindell committed
112
	#define SOCKET_DEBUG_FILEXFER	(1<<7)	// 0x80
113
#endif
114

115
116
117
118
119
120

typedef struct {
	SOCKET			socket;
	SOCKADDR_IN		client_addr;
} ftp_t;

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

static const char *mon[]={"Jan","Feb","Mar","Apr","May","Jun"
            ,"Jul","Aug","Sep","Oct","Nov","Dec"};

BOOL direxist(char *dir)
{
	if(access(dir,0)==0)
		return(TRUE);
	else
		return(FALSE);
}

BOOL dir_op(scfg_t* cfg, user_t* user, uint dirnum)
{
	return(user->level>=SYSOP_LEVEL || user->exempt&FLAG('R')
		|| (cfg->dir[dirnum]->op_ar[0] && chk_ar(cfg,cfg->dir[dirnum]->op_ar,user)));
}

139
static int lprintf(int level, char *fmt, ...)
140
141
142
143
144
{
	int		result;
	va_list argptr;
	char sbuf[1024];

145
    if(startup==NULL || startup->lputs==NULL)
146
        return(0);
147

148
    va_start(argptr,fmt);
149
150
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
151
    va_end(argptr);
152
    result=startup->lputs(startup->cbdata,level,sbuf);
153
154
155
156
157
158
159

	return(result);
}

#ifdef _WINSOCKAPI_

static WSADATA WSAData;
160
161
#define SOCKLIB_DESC WSAData.szDescription

162
static BOOL WSAInitialized=FALSE;
163
164
165
166
167
168

static BOOL winsock_startup(void)
{
	int		status;             /* Status Code */

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
169
		lprintf(LOG_INFO,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
170
		WSAInitialized=TRUE;
171
172
173
		return (TRUE);
	}

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

178
#else /* No WINSOCK */
179

180
#define winsock_startup()	(TRUE)
181
#define SOCKLIB_DESC		NULL
182
183
184
185
186
187

#endif

static void status(char* str)
{
	if(startup!=NULL && startup->status!=NULL)
188
	    startup->status(startup->cbdata,str);
189
190
191
192
193
}

static void update_clients(void)
{
	if(startup!=NULL && startup->clients!=NULL)
194
		startup->clients(startup->cbdata,active_clients);
195
196
}

197
static void client_on(SOCKET sock, client_t* client, BOOL update)
198
199
{
	if(startup!=NULL && startup->client_on!=NULL)
200
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
201
202
203
204
205
}

static void client_off(SOCKET sock)
{
	if(startup!=NULL && startup->client_on!=NULL)
206
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
207
208
}

209
static void thread_up(BOOL setuid)
210
{
rswindell's avatar
rswindell committed
211
	thread_count++;
212
	if(startup!=NULL && startup->thread_up!=NULL)
213
		startup->thread_up(startup->cbdata,TRUE, setuid);
214
215
216
217
}

static void thread_down(void)
{
rswindell's avatar
rswindell committed
218
219
	if(thread_count>0)
		thread_count--;
220
	if(startup!=NULL && startup->thread_up!=NULL)
221
		startup->thread_up(startup->cbdata,FALSE, FALSE);
222
223
}

224
static SOCKET ftp_open_socket(int type)
225
{
226
227
	SOCKET	sock;
	char	error[256];
228
229
230

	sock=socket(AF_INET, type, IPPROTO_IP);
	if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) 
231
		startup->socket_open(startup->cbdata,TRUE);
232
	if(sock!=INVALID_SOCKET) {
233
		if(set_socket_options(&scfg, sock, error))
234
			lprintf(LOG_ERR,"%04d !ERROR %s",sock, error);
235
236
		sockets++;
#ifdef _DEBUG
237
		lprintf(LOG_DEBUG,"%04d Socket opened (%u sockets in use)",sock,sockets);
238
239
240
241
242
243
244
245
#endif
	}
	return(sock);
}

#ifdef __BORLANDC__
#pragma argsused
#endif
246
static int ftp_close_socket(SOCKET* sock, int line)
247
248
249
250
{
	int		result;

	if((*sock)==INVALID_SOCKET) {
251
		lprintf(LOG_WARNING,"0000 !INVALID_SOCKET in close_socket from line %u",line);
252
253
254
		return(-1);
	}

255
256
	shutdown(*sock,SHUT_RDWR);	/* required on Unix */

257
	result=closesocket(*sock);
258
	if(startup!=NULL && startup->socket_open!=NULL) 
259
		startup->socket_open(startup->cbdata,FALSE);
260
261
262

	sockets--;

263
264
	if(result!=0) {
		if(ERROR_VALUE!=ENOTSOCK)
265
			lprintf(LOG_WARNING,"%04d !ERROR %d closing socket from line %u",*sock,ERROR_VALUE,line);
266
	} else if(sock==&server_socket || *sock==server_socket)
267
		lprintf(LOG_DEBUG,"%04d Server socket closed (%u sockets in use) from line %u",*sock,sockets,line);
268
#ifdef _DEBUG
269
	else 
270
		lprintf(LOG_DEBUG,"%04d Socket closed (%u sockets in use) from line %u",*sock,sockets,line);
271
272
273
274
275
276
277
278
279
#endif
	*sock=INVALID_SOCKET;

	return(result);
}

static int sockprintf(SOCKET sock, char *fmt, ...)
{
	int		len;
280
	int		maxlen;
281
282
283
	int		result;
	va_list argptr;
	char	sbuf[1024];
284
285
	fd_set	socket_set;
	struct timeval tv;
286
287

    va_start(argptr,fmt);
288
289
290
    len=vsnprintf(sbuf,maxlen=sizeof(sbuf)-2,fmt,argptr);
    va_end(argptr);

291
	if(len<0 || len>maxlen) /* format error or output truncated */
292
		len=maxlen;
293
	if(startup!=NULL && startup->options&FTP_OPT_DEBUG_TX)
294
295
		lprintf(LOG_DEBUG,"%04d TX: %.*s", sock, len, sbuf);
	memcpy(sbuf+len,"\r\n",2);
296
	len+=2;
297

298
	if(sock==INVALID_SOCKET) {
299
		lprintf(LOG_WARNING,"!INVALID SOCKET in call to sockprintf");
300
301
302
		return(0);
	}

303
	/* Check socket for writability (using select) */
304
	tv.tv_sec=300;
305
306
307
308
309
310
	tv.tv_usec=0;

	FD_ZERO(&socket_set);
	FD_SET(sock,&socket_set);

	if((result=select(sock+1,NULL,&socket_set,NULL,&tv))<1) {
311
		if(result==0)
312
			lprintf(LOG_WARNING,"%04d !TIMEOUT selecting socket for send"
313
314
				,sock);
		else
315
			lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket for send"
316
				,sock, ERROR_VALUE);
317
318
		return(0);
	}
319
	while((result=sendsocket(sock,sbuf,len))!=len) {
320
		if(result==SOCKET_ERROR) {
rswindell's avatar
rswindell committed
321
			if(ERROR_VALUE==EWOULDBLOCK) {
322
				YIELD();
323
324
				continue;
			}
rswindell's avatar
rswindell committed
325
			if(ERROR_VALUE==ECONNRESET) 
326
				lprintf(LOG_WARNING,"%04d Connection reset by peer on send",sock);
327
			else if(ERROR_VALUE==ECONNABORTED)
328
				lprintf(LOG_WARNING,"%04d Connection aborted by peer on send",sock);
329
			else
330
				lprintf(LOG_WARNING,"%04d !ERROR %d sending",sock,ERROR_VALUE);
331
332
			return(0);
		}
333
		lprintf(LOG_WARNING,"%04d !ERROR: short send: %u instead of %u",sock,result,len);
334
335
336
337
338
	}
	return(len);
}


339
340
341
342
343
344
345
346
/* Returns the directory index of a virtual lib/dir path (e.g. main/games/filename) */
int getdir(char* p, user_t* user)
{
	char*	tp;
	char	path[MAX_PATH+1];
	int		dir;
	int		lib;

rswindell's avatar
rswindell committed
347
	SAFECOPY(path,p);
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
	p=path;

	if(*p=='/') 
		p++;
	else if(!strncmp(p,"./",2))
		p+=2;

	tp=strchr(p,'/');
	if(tp) *tp=0;
	for(lib=0;lib<scfg.total_libs;lib++) {
		if(!chk_ar(&scfg,scfg.lib[lib]->ar,user))
			continue;
		if(!stricmp(scfg.lib[lib]->sname,p))
			break;
	}
	if(lib>=scfg.total_libs) 
		return(-1);

	if(tp!=NULL)
		p=tp+1;

	tp=strchr(p,'/');
	if(tp) *tp=0;
	for(dir=0;dir<scfg.total_dirs;dir++) {
		if(scfg.dir[dir]->lib!=lib)
			continue;
		if(dir!=scfg.sysop_dir && dir!=scfg.upload_dir 
			&& !chk_ar(&scfg,scfg.dir[dir]->ar,user))
			continue;
377
		if(!stricmp(scfg.dir[dir]->code_suffix,p))
378
379
380
381
382
383
384
385
			break;
	}
	if(dir>=scfg.total_dirs) 
		return(-1);

	return(dir);
}

386
387
388
389
390
/*********************************/
/* JavaScript Data and Functions */
/*********************************/
#ifdef JAVASCRIPT

391
392
js_server_props_t js_server_props;

393
394
395
396
static JSBool
js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
    uintN		i;
397
    JSString*	str=NULL;
398
399
400
401
402
403
404
405
406
407
408
409
	FILE*	fp;

	if((fp=(FILE*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

    for (i = 0; i < argc; i++) {
		str = JS_ValueToString(cx, argv[i]);
		if (!str)
		    return JS_FALSE;
		fprintf(fp,"%s",JS_GetStringBytes(str));
	}

410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
	if(str==NULL)
		*rval = JSVAL_VOID;
	else
		*rval = STRING_TO_JSVAL(str);
	return(JS_TRUE);
}

static JSBool
js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
	FILE*	fp;

	if((fp=(FILE*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

	js_write(cx,obj,argc,argv,rval);
	fprintf(fp,"\r\n");
427
428
429
430
431
	return(JS_TRUE);
}

static JSFunctionSpec js_global_functions[] = {
	{"write",           js_write,           1},		/* write to HTML file */
432
433
	{"writeln",         js_writeln,         1},		/* write to HTML file */
	{"print",			js_writeln,         1},		/* alias for writeln */
434
435
436
437
438
439
440
441
442
	{0}
};

static void
js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
	char	line[64];
	char	file[MAX_PATH+1];
	char*	warning;
rswindell's avatar
rswindell committed
443
444
445
	FILE*	fp;

	fp=(FILE*)JS_GetContextPrivate(cx);
446
447
	
	if(report==NULL) {
448
		lprintf(LOG_ERR,"!JavaScript: %s", message);
rswindell's avatar
rswindell committed
449
450
		if(fp!=NULL)
			fprintf(fp,"!JavaScript: %s", message);
451
452
453
454
455
456
457
458
459
		return;
    }

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

	if(report->lineno)
460
		sprintf(line," line %u",report->lineno);
461
462
463
464
465
466
467
468
469
470
471
	else
		line[0]=0;

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

472
	lprintf(LOG_ERR,"!JavaScript %s%s%s: %s",warning,file,line,message);
rswindell's avatar
rswindell committed
473
474
	if(fp!=NULL)
		fprintf(fp,"!JavaScript %s%s%s: %s",warning,file,line,message);
475
476
}

477
static JSContext* 
478
js_initcx(JSRuntime* runtime, SOCKET sock, JSObject** glob, JSObject** ftp)
479
480
481
{
	JSContext*	js_cx;
	JSObject*	js_glob;
482
	BOOL		success=FALSE;
483

484
	lprintf(LOG_DEBUG,"%04d JavaScript: Initializing context (stack: %lu bytes)"
485
		,sock,startup->js_cx_stack);
486

487
    if((js_cx = JS_NewContext(runtime, startup->js_cx_stack))==NULL)
488
489
		return(NULL);

490
	lprintf(LOG_DEBUG,"%04d JavaScript: Context created",sock);
rswindell's avatar
rswindell committed
491

492
493
    JS_SetErrorReporter(js_cx, js_ErrorReporter);

494
	do {
495

496
		lprintf(LOG_DEBUG,"%04d JavaScript: Initializing Global object",sock);
497
		if((js_glob=js_CreateGlobalObject(js_cx, &scfg, NULL))==NULL) 
498
			break;
499

500
501
		if (!JS_DefineFunctions(js_cx, js_glob, js_global_functions)) 
			break;
502

503
		lprintf(LOG_DEBUG,"%04d JavaScript: Initializing System object",sock);
504
		if(js_CreateSystemObject(js_cx, js_glob, &scfg, uptime, startup->host_name, SOCKLIB_DESC)==NULL) 
505
506
			break;

507
		if((*ftp=JS_DefineObject(js_cx, js_glob, "ftp", NULL
508
			,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))==NULL)
509
510
			break;

511
512
513
514
515
516
#if 0
		char		ver[256];
		JSObject*	server;
		JSString*	js_str;
		jsval		val;

517
		if((server=JS_DefineObject(js_cx, js_glob, "server", NULL
518
			,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))==NULL)
519
			break;
520

521
		sprintf(ver,"%s %s",FTP_SERVER,revision);
522
523
524
		if((js_str=JS_NewStringCopyZ(js_cx, ver))==NULL)
			break;
		val = STRING_TO_JSVAL(js_str);
525
		if(!JS_SetProperty(js_cx, server, "version", &val))
526
527
			break;

528
529
530
		if((js_str=JS_NewStringCopyZ(js_cx, ftp_ver()))==NULL)
			break;
		val = STRING_TO_JSVAL(js_str);
531
		if(!JS_SetProperty(js_cx, server, "version_detail", &val))
532
533
			break;

534
535
536
537
538
#else
		if(js_CreateServerObject(js_cx,js_glob,&js_server_props)==NULL)
			break;
#endif

539
540
541
542
543
544
		if(glob!=NULL)
			*glob=js_glob;

		success=TRUE;

	} while(0);
545

546
547
548
549
	if(!success) {
		JS_DestroyContext(js_cx);
		return(NULL);
	}
550
551
552
553
554

	return(js_cx);
}

BOOL js_add_file(JSContext* js_cx, JSObject* array, 
555
556
557
558
559
				 char* name, char* desc, char* ext_desc,
				 ulong size, ulong credits, 
				 time_t time, time_t uploaded, time_t last_downloaded, 
				 ulong times_downloaded, ulong misc, 
				 char* uploader, char* link)
560
561
{
	JSObject*	file;
562
	JSString*	js_str;
563
	jsval		val;
564
	jsuint		index;
565

566
	if((file=JS_NewObject(js_cx, NULL, NULL, NULL))==NULL)
567
568
		return(FALSE);

569
570
571
	if((js_str=JS_NewStringCopyZ(js_cx, name))==NULL)
		return(FALSE);
	val=STRING_TO_JSVAL(js_str);
572
573
574
	if(!JS_SetProperty(js_cx, file, "name", &val))
		return(FALSE);

575
576
577
	if((js_str=JS_NewStringCopyZ(js_cx, desc))==NULL)
		return(FALSE);
	val=STRING_TO_JSVAL(js_str);
578
579
580
	if(!JS_SetProperty(js_cx, file, "description", &val))
		return(FALSE);

581
582
583
	if((js_str=JS_NewStringCopyZ(js_cx, ext_desc))==NULL)
		return(FALSE);
	val=STRING_TO_JSVAL(js_str);
584
	if(!JS_SetProperty(js_cx, file, "extended_description", &val))
585
586
587
588
589
590
591
592
593
594
		return(FALSE);

	val=INT_TO_JSVAL(size);
	if(!JS_SetProperty(js_cx, file, "size", &val))
		return(FALSE);

	val=INT_TO_JSVAL(credits);
	if(!JS_SetProperty(js_cx, file, "credits", &val))
		return(FALSE);

595
	JS_NewNumberValue(js_cx,time,&val);
596
597
598
599
600
601
602
	if(!JS_SetProperty(js_cx, file, "time", &val))
		return(FALSE);

	val=INT_TO_JSVAL(uploaded);
	if(!JS_SetProperty(js_cx, file, "uploaded", &val))
		return(FALSE);

603
604
605
606
607
608
609
610
	val=INT_TO_JSVAL(last_downloaded);
	if(!JS_SetProperty(js_cx, file, "last_downloaded", &val))
		return(FALSE);

	val=INT_TO_JSVAL(times_downloaded);
	if(!JS_SetProperty(js_cx, file, "times_downloaded", &val))
		return(FALSE);

611
612
613
	if((js_str=JS_NewStringCopyZ(js_cx, uploader))==NULL)
		return(FALSE);
	val=STRING_TO_JSVAL(js_str);
614
615
616
	if(!JS_SetProperty(js_cx, file, "uploader", &val))
		return(FALSE);

617
	val=INT_TO_JSVAL(misc);
rswindell's avatar
rswindell committed
618
	if(!JS_SetProperty(js_cx, file, "settings", &val))
619
620
		return(FALSE);

621
622
623
	if((js_str=JS_NewStringCopyZ(js_cx, link))==NULL)
		return(FALSE);
	val=STRING_TO_JSVAL(js_str);
624
625
626
627
628
629
630
631
632
633
	if(!JS_SetProperty(js_cx, file, "link", &val))
		return(FALSE);

	if(!JS_GetArrayLength(js_cx, array, &index))
		return(FALSE);

	val=OBJECT_TO_JSVAL(file);
	return(JS_SetElement(js_cx, array, index, &val));
}

634
BOOL js_generate_index(JSContext* js_cx, JSObject* parent, 
635
636
637
638
639
640
					   SOCKET sock, FILE* fp, int lib, int dir, user_t* user)
{
	char		str[256];
	char		path[MAX_PATH+1];
	char		spath[MAX_PATH+1];	/* script path */
	char		vpath[MAX_PATH+1];	/* virtual path */
641
642
	char		aliasfile[MAX_PATH+1];
	char		extdesc[513];
643
644
645
	char*		p;
	char*		tp;
	char*		np;
646
	char*		dp;
647
	char		aliasline[512];
648
	BOOL		alias_dir;
649
650
651
652
653
654
655
	BOOL		success=FALSE;
	FILE*		alias_fp;
	uint		i;
	file_t		f;
	glob_t		g;
	jsval		val;
	jsval		rval;
656
657
	JSObject*	lib_obj=NULL;
	JSObject*	dir_obj=NULL;
658
659
660
	JSObject*	file_array=NULL;
	JSObject*	dir_array=NULL;
	JSScript*	js_script=NULL;
661
	JSString*	js_str;
662
663
664
665
666
667

	JS_SetContextPrivate(js_cx, fp);

	do {	/* pseudo try/catch */

		if((file_array=JS_NewArrayObject(js_cx, 0, NULL))==NULL) {
668
			lprintf(LOG_ERR,"%04d !JavaScript FAILED to create file_array",sock);
669
670
671
			break;
		}

672
673
674
		/* file[] */
		val=OBJECT_TO_JSVAL(file_array);
		if(!JS_SetProperty(js_cx, parent, "file_list", &val)) {
675
			lprintf(LOG_ERR,"%04d !JavaScript FAILED to set file property",sock);
676
677
678
			break;
		}

679
		if((dir_array=JS_NewArrayObject(js_cx, 0, NULL))==NULL) {
680
			lprintf(LOG_ERR,"%04d !JavaScript FAILED to create dir_array",sock);
681
682
683
			break;
		}

684
685
686
		/* dir[] */
		val=OBJECT_TO_JSVAL(dir_array);
		if(!JS_SetProperty(js_cx, parent, "dir_list", &val)) {
687
			lprintf(LOG_ERR,"%04d !JavaScript FAILED to set dir property",sock);
688
689
690
			break;
		}

691
692
693
694
695
		if(strcspn(startup->html_index_script,"/\\")==strlen(startup->html_index_script)) {
			sprintf(spath,"%s%s",scfg.mods_dir,startup->html_index_script);
			if(scfg.mods_dir[0]==0 || !fexist(spath))
				sprintf(spath,"%s%s",scfg.exec_dir,startup->html_index_script);
		} else
rswindell's avatar
rswindell committed
696
			sprintf(spath,"%.*s",(int)sizeof(spath)-4,startup->html_index_script);
697
		/* Add extension if not specified */
698
699
700
701
		if(!strchr(spath,'.'))
			strcat(spath,".js");

		if(!fexist(spath)) {
702
			lprintf(LOG_ERR,"%04d !HTML JavaScript (%s) doesn't exist",sock,spath);
703
704
705
			break;
		}

706
707
708
		if((js_str=JS_NewStringCopyZ(js_cx, startup->html_index_file))==NULL)
			break;
		val=STRING_TO_JSVAL(js_str);
709
		if(!JS_SetProperty(js_cx, parent, "html_index_file", &val)) {
710
			lprintf(LOG_ERR,"%04d !JavaScript FAILED to set html_index_file property",sock);
711
712
713
714
			break;
		}

		/* curlib */
715
		if((lib_obj=JS_NewObject(js_cx, NULL, 0, NULL))==NULL) {
716
			lprintf(LOG_ERR,"%04d !JavaScript FAILED to create lib_obj",sock);
717
718
			break;
		}
719

720
		val=OBJECT_TO_JSVAL(lib_obj);
721
		if(!JS_SetProperty(js_cx, parent, "curlib", &val)) {
722
			lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curlib property",sock);
723
			break;
724
		}
725
726

		/* curdir */
727
		if((dir_obj=JS_NewObject(js_cx, NULL, 0, NULL))==NULL) {
728
			lprintf(LOG_ERR,"%04d !JavaScript FAILED to create dir_obj",sock);
729
730
			break;
		}
731
732

		val=OBJECT_TO_JSVAL(dir_obj);
733
		if(!JS_SetProperty(js_cx, parent, "curdir", &val)) {
734
			lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir property",sock);
735
736
737
			break;
		}

738
		SAFECOPY(vpath,"/");
739
740
741
742

		if(lib>=0) { /* root */

			strcat(vpath,scfg.lib[lib]->sname);
743
			strcat(vpath,"/");
744

745
746
747
			if((js_str=JS_NewStringCopyZ(js_cx, scfg.lib[lib]->sname))==NULL)
				break;
			val=STRING_TO_JSVAL(js_str);
748
			if(!JS_SetProperty(js_cx, lib_obj, "name", &val)) {
749
				lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curlib.name property",sock);
750
751
752
				break;
			}

753
754
755
			if((js_str=JS_NewStringCopyZ(js_cx, scfg.lib[lib]->lname))==NULL)
				break;
			val=STRING_TO_JSVAL(js_str);
756
			if(!JS_SetProperty(js_cx, lib_obj, "description", &val)) {
757
				lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curlib.desc property",sock);
758
759
				break;
			}
760
		}
761
762

		if(dir>=0) { /* 1st level */
763
			strcat(vpath,scfg.dir[dir]->code_suffix);
764
765
			strcat(vpath,"/");

766
767
768
			if((js_str=JS_NewStringCopyZ(js_cx, scfg.dir[dir]->code))==NULL)
				break;
			val=STRING_TO_JSVAL(js_str);
769
			if(!JS_SetProperty(js_cx, dir_obj, "code", &val)) {
770
				lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir.code property",sock);
771
772
773
				break;
			}

774
775
776
			if((js_str=JS_NewStringCopyZ(js_cx, scfg.dir[dir]->sname))==NULL)
				break;
			val=STRING_TO_JSVAL(js_str);
777
			if(!JS_SetProperty(js_cx, dir_obj, "name", &val)) {
778
				lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir.name property",sock);
779
780
781
				break;
			}

782
783
784
			if((js_str=JS_NewStringCopyZ(js_cx, scfg.dir[dir]->lname))==NULL)
				break;
			val=STRING_TO_JSVAL(js_str);
785
			if(!JS_SetProperty(js_cx, dir_obj, "description", &val)) {
786
				lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir.desc property",sock);
787
788
789
790
				break;
			}

			val=INT_TO_JSVAL(scfg.dir[dir]->misc);
rswindell's avatar
rswindell committed
791
			if(!JS_SetProperty(js_cx, dir_obj, "settings", &val)) {
792
				lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir.misc property",sock);
793
794
				break;
			}
795
796
		}

797
798
799
		if((js_str=JS_NewStringCopyZ(js_cx, vpath))==NULL)
			break;
		val=STRING_TO_JSVAL(js_str);
800
		if(!JS_SetProperty(js_cx, parent, "path", &val)) {
801
			lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir property",sock);
802
803
804
805
806
807
808
809
810
811
			break;
		}

		if(lib<0) {	/* root dir */

			/* File Aliases */
			sprintf(path,"%sftpalias.cfg",scfg.ctrl_dir);
			if((alias_fp=fopen(path,"r"))!=NULL) {

				while(!feof(alias_fp)) {
812
					if(!fgets(aliasline,sizeof(aliasline),alias_fp))
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
						break;

					p=aliasline;	/* alias pointer */
					while(*p && *p<=' ') p++;

					if(*p==';')	/* comment */
						continue;

					tp=p;		/* terminator pointer */
					while(*tp && *tp>' ') tp++;
					if(*tp) *tp=0;

					np=tp+1;	/* filename pointer */
					while(*np && *np<=' ') np++;

828
829
830
					tp=np;		/* terminator pointer */
					while(*tp && *tp>' ') tp++;
					if(*tp) *tp=0;
831

832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
					dp=tp+1;	/* description pointer */
					while(*dp && *dp<=' ') dp++;
					truncsp(dp);

					alias_dir=FALSE;

					/* Virtual Path? */
					if(!strnicmp(np,BBS_VIRTUAL_PATH,strlen(BBS_VIRTUAL_PATH))) {
						if((dir=getdir(np+strlen(BBS_VIRTUAL_PATH),user))<0)
							continue; /* No access or invalid virtual path */
						tp=strrchr(np,'/');
						if(tp==NULL) 
							continue;
						tp++;
						if(*tp) {
847
							SAFEPRINTF2(aliasfile,"%s%s",scfg.dir[dir]->path,tp);
848
849
850
851
852
853
854
855
							np=aliasfile;
						}
						else 
							alias_dir=TRUE;
					}

					if(!alias_dir && !fexist(np))
						continue;
856

857
858
859
					if(alias_dir) {
						if(!chk_ar(&scfg,scfg.dir[dir]->ar,user))
							continue;
860
						SAFEPRINTF2(vpath,"/%s/%s",p,startup->html_index_file);
861
					} else
862
						SAFECOPY(vpath,p);
863
					js_add_file(js_cx
864
						,alias_dir ? dir_array : file_array
865
						,p				/* filename */
866
867
868
						,dp				/* description */
						,NULL			/* extdesc */
						,flength(np)	/* size */
869
						,0				/* credits */
870
871
						,fdate(np)		/* time */
						,fdate(np)		/* uploaded */
872
873
						,0				/* last downloaded */
						,0				/* times downloaded */
874
						,0				/* misc */
875
						,scfg.sys_id	/* uploader */
876
						,vpath			/* link */
877
878
879
880
881
882
883
884
885
						);
				}

				fclose(alias_fp);
			}

			/* QWK Packet */
			if(startup->options&FTP_OPT_ALLOW_QWK /* && fexist(qwkfile) */) {
				sprintf(str,"%s.qwk",scfg.sys_id);
886
				SAFEPRINTF(vpath,"/%s",str);
887
888
889
890
				js_add_file(js_cx
					,file_array 
					,str						/* filename */
					,"QWK Message Packet"		/* description */
891
					,NULL						/* extdesc */
892
893
					,10240						/* size */
					,0							/* credits */
894
895
					,time(NULL)					/* time */
					,0							/* uploaded */
896
897
					,0							/* last downloaded */
					,0							/* times downloaded */
898
					,0							/* misc */
899
					,scfg.sys_id				/* uploader */
900
					,str						/* link */
901
902
903
904
905
906
907
					);
			}

			/* Library Folders */
			for(i=0;i<scfg.total_libs;i++) {
				if(!chk_ar(&scfg,scfg.lib[i]->ar,user))
					continue;
908
				SAFEPRINTF2(vpath,"/%s/%s",scfg.lib[i]->sname,startup->html_index_file);
909
910
911
912
				js_add_file(js_cx
					,dir_array 
					,scfg.lib[i]->sname			/* filename */
					,scfg.lib[i]->lname			/* description */
913
914
					,NULL						/* extdesc */
					,0,0,0,0,0,0,0				/* unused */
915
					,scfg.sys_id				/* uploader */
916
					,vpath						/* link */
917
918
919
					);
			}
		} else if(dir<0) {
920
			/* Directories */
921
922
923
			for(i=0;i<scfg.total_dirs;i++) {
				if(scfg.dir[i]->lib!=lib)
					continue;
924
925
				if(/* i!=scfg.sysop_dir && i!=scfg.upload_dir && */
					!chk_ar(&scfg,scfg.dir[i]->ar,user))
926
					continue;
927
				SAFEPRINTF3(vpath,"/%s/%s/%s"
928
					,scfg.lib[scfg.dir[i]->lib]->sname
929
					,scfg.dir[i]->code_suffix
930
931
932
					,startup->html_index_file);
				js_add_file(js_cx
					,dir_array 
933
					,scfg.dir[i]->sname			/* filename */
934
					,scfg.dir[i]->lname			/* description */
935
					,NULL						/* extdesc */
936
937
938
					,getfiles(&scfg,i)			/* size */
					,0,0,0,0,0					/* unused */
					,scfg.dir[i]->misc			/* misc */
939
					,scfg.sys_id				/* uploader */
940
					,vpath						/* link */
941
942
943
944
					);

			}
		} else if(chk_ar(&scfg,scfg.dir[dir]->ar,user)){
945
			SAFEPRINTF(path,"%s*",scfg.dir[dir]->path);
946
947
948
949
950
951
952
			glob(path,0,NULL,&g);
			for(i=0;i<(int)g.gl_pathc;i++) {
				if(isdir(g.gl_pathv[i]))
					continue;
	#ifdef _WIN32
				GetShortPathName(g.gl_pathv[i], str, sizeof(str));
	#else
953
				SAFECOPY(str,g.gl_pathv[i]);
954
955
956
957
958
959
	#endif
				padfname(getfname(str),f.name);
				f.dir=dir;
				if(getfileixb(&scfg,&f)) {
					f.size=0; /* flength(g.gl_pathv[i]); */
					getfiledat(&scfg,&f);
rswindell's avatar
rswindell committed
960
961
					if(f.misc&FM_EXTDESC) {
						extdesc[0]=0;
962
						getextdesc(&scfg, dir, f.datoffset, extdesc);
rswindell's avatar
rswindell committed
963
964
965
						/* Remove Ctrl-A Codes and Ex-ASCII code */
						remove_ctrl_a(extdesc,NULL);
					}
966
					SAFEPRINTF3(vpath,"/%s/%s/%s"
967
						,scfg.lib[scfg.dir[dir]->lib]->sname
968
						,scfg.dir[dir]->code_suffix
969
970
971
972
973
						,getfname(g.gl_pathv[i]));
					js_add_file(js_cx
						,file_array 
						,getfname(g.gl_pathv[i])	/* filename */
						,f.desc						/* description */
974
						,f.misc&FM_EXTDESC ? extdesc : NULL
975
976
						,f.size						/* size */
						,f.cdt						/* credits */
977
978
						,f.date						/* time */
						,f.dateuled					/* uploaded */
979
980
						,f.datedled					/* last downloaded */
						,f.timesdled				/* times downloaded */
981
						,f.misc						/* misc */
982
						,f.uler						/* uploader */
983
						,getfname(g.gl_pathv[i])	/* link */
984
985
986
987
988
989
						);
				}
			}
			globfree(&g);
		}

990

991
		/* RUN SCRIPT */
992
993
		JS_ClearPendingException(js_cx);

994
		if((js_script=JS_CompileFile(js_cx, parent, spath))==NULL) {
995
			lprintf(LOG_ERR,"%04d !JavaScript FAILED to compile script (%s)",sock,spath);
996
997
998
			break;
		}

999
		if((success=JS_ExecuteScript(js_cx, parent, js_script, &rval))!=TRUE) {
1000
			lprintf(LOG_ERR,"%04d !JavaScript FAILED to execute script (%s)",sock,spath);
For faster browsing, not all history is shown. View entire blame