Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* 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) *
* *
* Copyright 2000 Rob Swindell - http://www.synchro.net/copyright.html *
* *
* 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. *
****************************************************************************/
/* Platform-specific headers */
#ifdef _WIN32
#include <share.h> /* SH_DENYNO */
#include <direct.h> /* _mkdir/_rmdir() */
#include <process.h> /* _beginthread */
#include <windows.h> /* required for mmsystem.h */
#include <mmsystem.h> /* SND_ASYNC */
#elif defined(__unix__)
#include <signal.h> /* signal/SIGPIPE */
#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 */
#include "sbbs.h"
#include "telnet.h"
/* Constants */
#define FTP_SERVER "Synchronet FTP Server"
#define FTP_VERSION "1.10"
#define ILLEGAL_FILENAME_CHARS "\\/|<>+[]:=\";,%"
#define STATUS_WFC "Listening"
#define BBS_VIRTUAL_PATH "bbs:/""/" /* this is actually bbs:<slash><slash> */
#define LOCAL_FSYS_DIR "local:"
#define BBS_FSYS_DIR "bbs:"
#define TIMEOUT_THREAD_WAIT 15 /* Seconds */
#define TIMEOUT_SOCKET_LISTEN 30 /* Seconds */
#define XFER_REPORT_INTERVAL 60 /* Seconds */
#define INDEX_FNAME_LEN 15
#define NAME_LEN 15 /* User name length for listings */
static ftp_startup_t* startup=NULL;
static scfg_t scfg;
static SOCKET server_socket=INVALID_SOCKET;
static DWORD active_clients=0;
static DWORD sockets=0;
static HANDLE socket_mutex=NULL;
static BYTE socket_debug[20000]={0};
#define SOCKET_DEBUG_CTRL (1<<0) // 0x01
#define SOCKET_DEBUG_CONNECT (1<<1) // 0x02
#define SOCKET_DEBUG_READLINE (1<<2) // 0x04
#define SOCKET_DEBUG_ACCEPT (1<<3) // 0x08
#define SOCKET_DEBUG_DOWNLOAD (1<<4) // 0x10
#define SOCKET_DEBUG_SELECT (1<<5) // 0x20
#define SOCKET_DEBUG_RECV_CHAR (1<<6) // 0x40
#define SOCKET_DEBUG_RECV_BUF (1<<7) // 0x80
typedef struct {
SOCKET socket;
SOCKADDR_IN client_addr;
} ftp_t;
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)));
}
static int lprintf(char *fmt, ...)
{
int result;
va_list argptr;
char sbuf[1024];
if(startup!=NULL && startup->lputs==NULL)
return(0);
va_start(argptr,fmt);
vsprintf(sbuf,fmt,argptr);
va_end(argptr);
result=startup->lputs(sbuf);
return(result);
}
#ifdef _WINSOCKAPI_
static WSADATA WSAData;
static BOOL WSAInitialized=FALSE;
static BOOL winsock_startup(void)
{
int status; /* Status Code */
if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
lprintf("%s %s",WSAData.szDescription, WSAData.szSystemStatus);
WSAInitialized=TRUE;
return (TRUE);
}
lprintf("!WinSock startup ERROR %d", status);
return (FALSE);
}
#else /* No WINSOCK */
#define winsock_startup() (TRUE)
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#endif
static void status(char* str)
{
if(startup!=NULL && startup->status!=NULL)
startup->status(str);
}
static void update_clients(void)
{
if(startup!=NULL && startup->clients!=NULL)
startup->clients(active_clients);
}
static void client_on(SOCKET sock, client_t* client)
{
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(TRUE,sock,client);
}
static void client_off(SOCKET sock)
{
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(FALSE,sock,NULL);
}
static void thread_up(void)
{
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(TRUE);
}
static void thread_down(void)
{
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(FALSE);
}
static SOCKET open_socket(int type)
sock=socket(AF_INET, type, IPPROTO_IP);
if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(TRUE);
if(sock!=INVALID_SOCKET) {
sockets++;
#ifdef _DEBUG
lprintf("%04d Socket opened (%d sockets in use)",sock,sockets);
#endif
}
return(sock);
}
#ifdef __BORLANDC__
#pragma argsused
#endif
static int close_socket(SOCKET* sock, int line)
if((result=WaitForSingleObject(socket_mutex,5000))!=WAIT_OBJECT_0)
lprintf("%04d !ERROR %d getting socket mutex from line %d"
,*sock,ERROR_VALUE,line);
if(IsBadWritePtr(sock,sizeof(SOCKET))) {
ReleaseMutex(socket_mutex);
lprintf("0000 !BAD socket pointer in close_socket from line %d",line);
if((*sock)==INVALID_SOCKET) {
ReleaseMutex(socket_mutex);
lprintf("0000 !INVALID_SOCKET in close_socket from line %d",line);
shutdown(*sock,SHUT_RDWR); /* required on Unix */
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
result=closesocket(*sock);
if(/* result==0 && */ startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(FALSE);
sockets--;
if(result!=0)
lprintf("%04d !ERROR %d closing socket from line %d",*sock,ERROR_VALUE,line);
else {
#ifdef _DEBUG
lprintf("%04d Socket closed (%d sockets in use) from line %d",*sock,sockets,line);
#endif
}
*sock=INVALID_SOCKET;
ReleaseMutex(socket_mutex);
return(result);
}
static int sockprintf(SOCKET sock, char *fmt, ...)
{
int len;
int result;
va_list argptr;
char sbuf[1024];
va_start(argptr,fmt);
len=vsprintf(sbuf,fmt,argptr);
if(startup->options&FTP_OPT_DEBUG_TX)
lprintf("%04d TX: %s", sock, sbuf);
strcat(sbuf,"\r\n");
len+=2;
va_end(argptr);
while((result=send(sock,sbuf,len,0))!=len) {
if(result==SOCKET_ERROR) {
lprintf("%04d Connection reset by peer on send",sock);
else if(ERROR_VALUE==ECONNABORTED)
lprintf("%04d Connection aborted by peer on send",sock);
else
lprintf("%04d !ERROR %d sending",sock,ERROR_VALUE);
return(0);
}
lprintf("%04d !ERROR: short send: %d instead of %d",sock,result,len);
}
return(len);
}
/************************************************/
/* Truncates white-space chars off end of 'str' */
/************************************************/

rswindell
committed
static void truncsp(char *str)
{
uint c;
c=strlen(str);
while(c && (uchar)str[c-1]<=' ') c--;
str[c]=0;
}
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
/* 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;
sprintf(path,"%.*s",(int)sizeof(path)-1,p);
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;
if(!stricmp(scfg.dir[dir]->code,p))
break;
}
if(dir>=scfg.total_dirs)
return(-1);
return(dir);
}
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
/*********************************/
/* JavaScript Data and Functions */
/*********************************/
#ifdef JAVASCRIPT
JSRuntime* js_runtime=NULL;
static JSBool
js_load(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char path[MAX_PATH+1];
uintN i;
JSString* str;
const char* filename;
JSScript* script;
JSBool ok;
jsval result;
// JSErrorReporter older;
for (i = 0; i < argc; i++) {
str = JS_ValueToString(cx, argv[i]);
if (!str)
return JS_FALSE;
argv[i] = STRING_TO_JSVAL(str);
filename = JS_GetStringBytes(str);
errno = 0;
// older = JS_SetErrorReporter(cx, my_LoadErrorReporter);
if(!strchr(filename,BACKSLASH))
sprintf(path,"%s%s",scfg.exec_dir,filename);
else
strcpy(path,filename);
script = JS_CompileFile(cx, obj, path);
if (!script)
ok = JS_FALSE;
else {
ok = JS_ExecuteScript(cx, obj, script, &result);
JS_DestroyScript(cx, script);
}
// JS_SetErrorReporter(cx, older);
if (!ok)
return JS_FALSE;
}
return JS_TRUE;
}
static JSBool
js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
uintN i;
JSString * str;
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));
}
return(JS_TRUE);
}
static JSBool
js_format(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char tmp[1024];
uintN i;
JSString * fmt;
JSString * str;
va_list arglist[64];
fmt = JS_ValueToString(cx, argv[0]);
if (!fmt)
return JS_FALSE;
for (i = 1; i < argc && i<sizeof(arglist)/sizeof(arglist[0]); i++) {
if(JSVAL_IS_STRING(argv[i])) {
str = JS_ValueToString(cx, argv[i]);
if (!str)
return JS_FALSE;
arglist[i-1]=JS_GetStringBytes(str);
} else if(JSVAL_IS_INT(argv[i]))
arglist[i-1]=(char *)JSVAL_TO_INT(argv[i]);
else
arglist[i-1]=NULL;
453
454
455
456
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
}
vsprintf(tmp,JS_GetStringBytes(fmt),(char*)arglist);
str = JS_NewStringCopyZ(cx, tmp);
*rval = STRING_TO_JSVAL(str);
return JS_TRUE;
}
static JSClass js_global_class = {
"Global",0,
JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,
JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,JS_FinalizeStub
};
static JSFunctionSpec js_global_functions[] = {
{"load", js_load, 1}, /* Load and execute a javascript file */
{"write", js_write, 1}, /* write to HTML file */
{"format", js_format, 1}, /* return a formatted string */
{0}
};
static void
js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
char line[64];
char file[MAX_PATH+1];
char* warning;
FILE* fp;
fp=(FILE*)JS_GetContextPrivate(cx);
if(report==NULL) {
lprintf("!JavaScript: %s", message);
if(fp!=NULL)
fprintf(fp,"!JavaScript: %s", message);
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="";
lprintf("!JavaScript %s%s%s: %s",warning,file,line,message);
if(fp!=NULL)
fprintf(fp,"!JavaScript %s%s%s: %s",warning,file,line,message);
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
}
JSContext* js_initcx(JSObject** glob)
{
char ver[256];
JSContext* js_cx;
JSObject* js_glob;
JSObject* sysobj;
jsval val;
if((js_cx = JS_NewContext(js_runtime, JAVASCRIPT_CONTEXT_STACK))==NULL)
return(NULL);
JS_SetErrorReporter(js_cx, js_ErrorReporter);
if((js_glob = JS_NewObject(js_cx, &js_global_class, NULL, NULL))==NULL) {
JS_DestroyContext(js_cx);
return(NULL);
}
if (!JS_InitStandardClasses(js_cx, js_glob)) {
JS_DestroyContext(js_cx);
return(NULL);
}
if (!JS_DefineFunctions(js_cx, js_glob, js_global_functions)) {
JS_DestroyContext(js_cx);
return(NULL);
}
if((sysobj=CreateSystemObject(&scfg, js_cx, js_glob))==NULL) {
JS_DestroyContext(js_cx);
return(NULL);
}
sprintf(ver,"%s v%s",FTP_SERVER,FTP_VERSION);
val = STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, ver));
if(!JS_SetProperty(js_cx, sysobj, "version", &val))
return(FALSE);
val = STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, ftp_ver()));
if(!JS_SetProperty(js_cx, sysobj, "version_detail", &val))
return(FALSE);
if(glob!=NULL)
*glob=js_glob;
return(js_cx);
}
static JSClass js_file_class = {
"File",0,
JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,
JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,JS_FinalizeStub
};
BOOL js_add_file(JSContext* js_cx, JSObject* array,
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)
{
JSObject* file;
jsval val;
jsint index;
if((file=JS_NewObject(js_cx, &js_file_class, NULL, NULL))==NULL)
return(FALSE);
val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, name));
if(!JS_SetProperty(js_cx, file, "name", &val))
return(FALSE);
val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, desc));
if(!JS_SetProperty(js_cx, file, "description", &val))
return(FALSE);
val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, ext_desc));
if(!JS_SetProperty(js_cx, file, "extended_description", &val))
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);
val=INT_TO_JSVAL(time);
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);
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);
val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, uploader));
if(!JS_SetProperty(js_cx, file, "uploader", &val))
return(FALSE);
val=INT_TO_JSVAL(misc);
if(!JS_SetProperty(js_cx, file, "settings", &val))
return(FALSE);
val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, link));
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));
}
BOOL js_generate_index(JSContext* js_cx, JSObject* js_glob,
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 */
char aliasfile[MAX_PATH+1];
char extdesc[513];
BOOL alias_dir;
BOOL success=FALSE;
FILE* alias_fp;
uint i;
file_t f;
glob_t g;
jsval val;
jsval rval;
JSObject* lib_obj=NULL;
JSObject* dir_obj=NULL;
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
JSObject* file_array=NULL;
JSObject* dir_array=NULL;
JSScript* js_script=NULL;
JS_SetContextPrivate(js_cx, fp);
do { /* pseudo try/catch */
if((file_array=JS_NewArrayObject(js_cx, 0, NULL))==NULL) {
lprintf("%04d !JavaScript FAILED to create file_array",sock);
break;
}
if((dir_array=JS_NewArrayObject(js_cx, 0, NULL))==NULL) {
lprintf("%04d !JavaScript FAILED to create dir_array",sock);
break;
}
/* Add extension if not specified */
if(!strchr(startup->html_index_script,BACKSLASH))
sprintf(spath,"%s%s",scfg.exec_dir,startup->html_index_script);
else
sprintf(spath,"%.*s",sizeof(spath)-4,startup->html_index_script);
if(!strchr(spath,'.'))
strcat(spath,".js");
if(!fexist(spath)) {
lprintf("%04d !HTML JavaScript (%s) doesn't exist",sock,spath);
break;
}
val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, startup->html_index_file));
if(!JS_SetProperty(js_cx, js_glob, "html_index_file", &val)) {
lprintf("%04d !JavaScript FAILED to set html_index_file property",sock);
break;
}
/* file[] */
if(!JS_SetProperty(js_cx, js_glob, "file_list", &val)) {
lprintf("%04d !JavaScript FAILED to set file property",sock);
break;
}
/* dir[] */
if(!JS_SetProperty(js_cx, js_glob, "dir_list", &val)) {
lprintf("%04d !JavaScript FAILED to set dir property",sock);
break;
}
/* curlib */
if((lib_obj=JS_NewObject(js_cx, &js_global_class, 0, NULL))==NULL) {
lprintf("%04d !JavaScript FAILED to create lib_obj",sock);
break;
}
val=OBJECT_TO_JSVAL(lib_obj);
if(!JS_SetProperty(js_cx, js_glob, "curlib", &val)) {
lprintf("%04d !JavaScript FAILED to set curlib property",sock);
break;
/* curdir */
if((dir_obj=JS_NewObject(js_cx, &js_global_class, 0, NULL))==NULL) {
lprintf("%04d !JavaScript FAILED to create dir_obj",sock);
val=OBJECT_TO_JSVAL(dir_obj);
if(!JS_SetProperty(js_cx, js_glob, "curdir", &val)) {
lprintf("%04d !JavaScript FAILED to set curdir property",sock);
strcpy(vpath,"/");
if(lib>=0) { /* root */
strcat(vpath,scfg.lib[lib]->sname);
val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, scfg.lib[lib]->sname));
if(!JS_SetProperty(js_cx, lib_obj, "name", &val)) {
lprintf("%04d !JavaScript FAILED to set curlib.name property",sock);
break;
}
val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, scfg.lib[lib]->lname));
if(!JS_SetProperty(js_cx, lib_obj, "description", &val)) {
lprintf("%04d !JavaScript FAILED to set curlib.desc property",sock);
break;
}
if(dir>=0) { /* 1st level */
strcat(vpath,scfg.dir[dir]->code);
strcat(vpath,"/");
val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, scfg.dir[dir]->code));
if(!JS_SetProperty(js_cx, dir_obj, "code", &val)) {
lprintf("%04d !JavaScript FAILED to set curdir.code property",sock);
break;
}
val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, scfg.dir[dir]->sname));
if(!JS_SetProperty(js_cx, dir_obj, "name", &val)) {
lprintf("%04d !JavaScript FAILED to set curdir.name property",sock);
break;
}
val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, scfg.dir[dir]->lname));
if(!JS_SetProperty(js_cx, dir_obj, "description", &val)) {
lprintf("%04d !JavaScript FAILED to set curdir.desc property",sock);
break;
}
val=INT_TO_JSVAL(scfg.dir[dir]->misc);
if(!JS_SetProperty(js_cx, dir_obj, "settings", &val)) {
lprintf("%04d !JavaScript FAILED to set curdir.misc property",sock);
break;
}
}
val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, vpath));
if(!JS_SetProperty(js_cx, js_glob, "ftp_path", &val)) {
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
lprintf("%04d !JavaScript FAILED to set curdir property",sock);
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)) {
if(!fgets(aliasline,sizeof(aliasline)-1,alias_fp))
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++;
tp=np; /* terminator pointer */
while(*tp && *tp>' ') tp++;
if(*tp) *tp=0;
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) {
sprintf(aliasfile,"%s%s",scfg.dir[dir]->path,tp);
np=aliasfile;
}
else
alias_dir=TRUE;
}
if(!alias_dir && !fexist(np))
continue;
if(alias_dir) {
if(!chk_ar(&scfg,scfg.dir[dir]->ar,user))
continue;
sprintf(vpath,"/%s/%s",p,startup->html_index_file);
strcpy(vpath,p);
,alias_dir ? dir_array : file_array
,dp /* description */
,NULL /* extdesc */
,flength(np) /* size */
,fdate(np) /* time */
,fdate(np) /* uploaded */
,0 /* last downloaded */
,0 /* times downloaded */
,0 /* misc */
,vpath /* link */
);
}
fclose(alias_fp);
}
/* QWK Packet */
if(startup->options&FTP_OPT_ALLOW_QWK /* && fexist(qwkfile) */) {
sprintf(str,"%s.qwk",scfg.sys_id);
sprintf(vpath,"/%s",str);
js_add_file(js_cx
,file_array
,str /* filename */
,"QWK Message Packet" /* description */
,NULL /* extdesc */
,time(NULL) /* time */
,0 /* uploaded */
,0 /* last downloaded */
,0 /* times downloaded */
,0 /* misc */
,str /* link */
);
}
/* Library Folders */
for(i=0;i<scfg.total_libs;i++) {
if(!chk_ar(&scfg,scfg.lib[i]->ar,user))
continue;
sprintf(vpath,"/%s/%s",scfg.lib[i]->sname,startup->html_index_file);
js_add_file(js_cx
,dir_array
,scfg.lib[i]->sname /* filename */
,scfg.lib[i]->lname /* description */
,NULL /* extdesc */
,0,0,0,0,0,0,0 /* unused */
,vpath /* link */
for(i=0;i<scfg.total_dirs;i++) {
if(scfg.dir[i]->lib!=lib)
continue;
if(/* i!=scfg.sysop_dir && i!=scfg.upload_dir && */
!chk_ar(&scfg,scfg.dir[i]->ar,user))
continue;
sprintf(vpath,"/%s/%s/%s"
,scfg.lib[scfg.dir[i]->lib]->sname
,scfg.dir[i]->code
,startup->html_index_file);
js_add_file(js_cx
,dir_array
,scfg.dir[i]->sname /* filename */
,NULL /* extdesc */
,getfiles(&scfg,i) /* size */
,0,0,0,0,0 /* unused */
,scfg.dir[i]->misc /* misc */
,vpath /* link */
);
}
} else if(chk_ar(&scfg,scfg.dir[dir]->ar,user)){
sprintf(path,"%s*",scfg.dir[dir]->path);
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
strcpy(str,g.gl_pathv[i]);
#endif
padfname(getfname(str),f.name);
strupr(f.name);
f.dir=dir;
if(getfileixb(&scfg,&f)) {
f.size=0; /* flength(g.gl_pathv[i]); */
getfiledat(&scfg,&f);
getextdesc(&scfg, dir, f.datoffset, extdesc);
/* Remove Ctrl-A Codes and Ex-ASCII code */
remove_ctrl_a(extdesc,NULL);
}
sprintf(vpath,"/%s/%s/%s"
,scfg.lib[scfg.dir[dir]->lib]->sname
,scfg.dir[dir]->code
,getfname(g.gl_pathv[i]));
js_add_file(js_cx
,file_array
,getfname(g.gl_pathv[i]) /* filename */
,f.desc /* description */
,f.misc&FM_EXTDESC ? extdesc : NULL
,f.size /* size */
,f.cdt /* credits */
,f.date /* time */
,f.dateuled /* uploaded */
,f.datedled /* last downloaded */
,f.timesdled /* times downloaded */
,f.misc /* misc */
,getfname(g.gl_pathv[i]) /* link */
);
}
}
globfree(&g);
}
/* RUN SCRIPT */
if((js_script=JS_CompileFile(js_cx, js_glob, spath))==NULL) {
lprintf("%04d !JavaScript FAILED to compile script (%s)",sock,spath);
break;
}
if((success=JS_ExecuteScript(js_cx, js_glob, js_script, &rval))!=TRUE) {
lprintf("%04d !JavaScript FAILED to execute script (%s)",sock,spath);
break;
}
} while(0);
if(js_script!=NULL)
JS_DestroyScript(js_cx, js_script);
JS_DeleteProperty(js_cx, js_glob, "ftp_path");
JS_DeleteProperty(js_cx, js_glob, "ftp_sort");
JS_DeleteProperty(js_cx, js_glob, "ftp_reverse");
JS_DeleteProperty(js_cx, js_glob, "file_list");
JS_DeleteProperty(js_cx, js_glob, "dir_list");
JS_DeleteProperty(js_cx, js_glob, "curlib");
JS_DeleteProperty(js_cx, js_glob, "curdir");
JS_DeleteProperty(js_cx, js_glob, "html_index_file");
return(success);
}
#endif
static int nopen(char *str, int access)
{
int file,share,count=0;
if(access&O_DENYNONE) {
share=SH_DENYNO;
access&=~O_DENYNONE; }
else if(access==O_RDONLY) share=SH_DENYWR;
else share=SH_DENYRW;

rswindell
committed
while(((file=sopen(str,O_BINARY|access,share))==-1)
&& errno==EACCES && count++<LOOP_NOPEN)
if(count>10)
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
return(file);
}
time_t gettimeleft(scfg_t* cfg, user_t* user, time_t starttime)
{
time_t now;
long tleft;
time_t timeleft;
now=time(NULL);
if(user->exempt&FLAG('T')) { /* Time online exemption */
timeleft=cfg->level_timepercall[user->level]*60;
if(timeleft<10) /* never get below 10 for exempt users */
timeleft=10; }
else {
tleft=(((long)cfg->level_timeperday[user->level]-user->ttoday)
+user->textra)*60L;
if(tleft<0) tleft=0;
if(tleft>cfg->level_timepercall[user->level]*60)
tleft=cfg->level_timepercall[user->level]*60;
tleft+=user->min*60L;
tleft-=now-starttime;
if(tleft>0x7fffL)
timeleft=0x7fff;
else
timeleft=tleft; }
return(timeleft);
}
static time_t checktime(void)
{
struct tm tm;
memset(&tm,0,sizeof(tm));
tm.tm_year=94;
tm.tm_mday=1;

rswindell
committed
return(mktime(&tm)-0x2D24BD00L);
}
BOOL upload_stats(ulong bytes)
{
char str[MAX_PATH+1];
int file;
ulong val;

rswindell
committed
sprintf(str,"%sdsts.dab",scfg.ctrl_dir);
if((file=nopen(str,O_RDWR))==-1)
return(FALSE);
lseek(file,20L,SEEK_SET); /* Skip timestamp, logons and logons today */
read(file,&val,4); /* Uploads today */
val++;
lseek(file,-4L,SEEK_CUR);
write(file,&val,4);
read(file,&val,4); /* Upload bytes today */
val+=bytes;
lseek(file,-4L,SEEK_CUR);
write(file,&val,4);
close(file);
return(TRUE);
}
BOOL download_stats(ulong bytes)
{
char str[MAX_PATH+1];
int file;
ulong val;

rswindell
committed
sprintf(str,"%sdsts.dab",scfg.ctrl_dir);
if((file=nopen(str,O_RDWR))==-1)
return(FALSE);
lseek(file,28L,SEEK_SET); /* Skip timestamp, logons and logons today */
read(file,&val,4); /* Downloads today */
val++;
lseek(file,-4L,SEEK_CUR);
write(file,&val,4);
read(file,&val,4); /* Download bytes today */
val+=bytes;
lseek(file,-4L,SEEK_CUR);
write(file,&val,4);
close(file);
return(TRUE);
}
void recverror(SOCKET socket, int rd, int line)
lprintf("%04d Socket closed by peer on receive (line %d)"
,socket, line);
lprintf("%04d Connection reset by peer on receive (line %d)"
,socket, line);
lprintf("%04d Connection aborted by peer on receive (line %d)"
,socket, line);
lprintf("%04d !ERROR %d receiving on socket (line %d)"
,socket, ERROR_VALUE, line);
lprintf("%04d !ERROR: recv on socket returned unexpected value: %d (line %d)"
,socket, rd, line);
}
int sockreadline(SOCKET socket, char* buf, int len, time_t* lastactive)
{
char ch;
int i,rd=0;

rswindell
committed
fd_set socket_set;
struct timeval tv;

rswindell
committed

rswindell
committed
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(socket,&socket_set);
i=select(socket+1,&socket_set,NULL,NULL,&tv);
if(server_socket==INVALID_SOCKET) {
sockprintf(socket,"421 Server downed, aborting.");
lprintf("%04d Server downed, aborting.",socket);
return(0);
}
if(i<1) {

rswindell
committed
if(i==0) {
if((time(NULL)-(*lastactive))>startup->max_inactivity) {
lprintf("%04d Disconnecting due to to inactivity.",socket);
sockprintf(socket,"421 Disconnecting due to inactivity (%u seconds)."
,startup->max_inactivity);
return(0);
}
socket_debug[socket]|=SOCKET_DEBUG_RECV_CHAR;

rswindell
committed
i=recv(socket, &ch, 1, 0);
socket_debug[socket]&=~SOCKET_DEBUG_RECV_CHAR;

rswindell
committed
if(i<1) {
#if 0
if(ERROR_VALUE==EWOULDBLOCK) {
mswait(1);
continue;
}
#endif

rswindell
committed
return(i);
}
if(ch=='\n' && rd>=1) {
break;
}
buf[rd++]=ch;
}
buf[rd-1]=0;
return(rd);
}
void DLLCALL ftp_terminate(void)
{
if(server_socket!=INVALID_SOCKET) {
lprintf("%04d FTP Terminate: closing socket",server_socket);
close_socket(&server_socket,__LINE__);
server_socket=INVALID_SOCKET;
}
}
typedef struct {
SOCKET ctrl_sock;
SOCKET* data_sock;
BOOL* inprogress;
BOOL* aborted;
BOOL delfile;
BOOL tmpfile;
BOOL credits;
BOOL append;
long filepos;
char filename[MAX_PATH+1];
time_t* lastactive;
user_t* user;
int dir;
char* desc;
} xfer_t;
static void send_thread(void* arg)
{
char buf[8192];
char fname[MAX_PATH+1];
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
int rd;
int wr;
ulong total=0;
ulong dur;
ulong cps;
ulong length;
BOOL error=FALSE;
FILE* fp;
file_t f;
xfer_t xfer;
time_t now;
time_t start;
time_t last_report;
xfer=*(xfer_t*)arg;
length=flength(xfer.filename);
if((fp=fopen(xfer.filename,"rb"))==NULL) {
lprintf("%04d !DATA ERROR %d opening %s",xfer.ctrl_sock,errno,xfer.filename);
sockprintf(xfer.ctrl_sock,"450 ERROR %d opening %s.",errno,xfer.filename);
if(xfer.tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES))
remove(xfer.filename);
close_socket(xfer.data_sock,__LINE__);
*xfer.inprogress=FALSE;
return;
}
thread_up();
*xfer.inprogress=TRUE;
*xfer.aborted=FALSE;
if(startup->options&FTP_OPT_DEBUG_DATA || xfer.filepos)
lprintf("%04d DATA socket %d sending %s from offset %ld"
,xfer.ctrl_sock,*xfer.data_sock,xfer.filename,xfer.filepos);
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
fseek(fp,xfer.filepos,SEEK_SET);
last_report=start=time(NULL);
while(!feof(fp)) {
now=time(NULL);
if(total && now>=last_report+XFER_REPORT_INTERVAL) {
lprintf("%04d Sent %ld bytes (%ld total) of %s (%lu cps)"
,xfer.ctrl_sock,total,length,xfer.filename
,now-start ? total/(now-start) : total*2);
last_report=now;
}
if(*xfer.aborted==TRUE) {
lprintf("%04d !DATA Transfer aborted",xfer.ctrl_sock);
sockprintf(xfer.ctrl_sock,"426 Transfer aborted.");
error=TRUE;
break;
}
if(server_socket==INVALID_SOCKET) {
lprintf("%04d !DATA Transfer locally aborted",xfer.ctrl_sock);
sockprintf(xfer.ctrl_sock,"426 Transfer locally aborted.");
error=TRUE;
break;
}
rd=fread(buf,sizeof(char),sizeof(buf),fp);
wr=send(*xfer.data_sock,buf,rd,0);
if(wr!=rd) {
if(wr==SOCKET_ERROR) {
lprintf("%04d DATA Connection reset by peer, sending on socket %d"
,xfer.ctrl_sock,*xfer.data_sock);
else if(ERROR_VALUE==ECONNABORTED)
lprintf("%04d DATA Connection aborted by peer, sending on socket %d"
,xfer.ctrl_sock,*xfer.data_sock);
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
else
lprintf("%04d !DATA ERROR %d sending on data socket %d"
,xfer.ctrl_sock,ERROR_VALUE,*xfer.data_sock);
sockprintf(xfer.ctrl_sock,"426 Error %d sending on DATA channel",ERROR_VALUE);
error=TRUE;
break;
}
if(wr==0) {
lprintf("%04d !DATA socket %d disconnected",xfer.ctrl_sock, *xfer.data_sock);
sockprintf(xfer.ctrl_sock,"426 DATA channel disconnected");
error=TRUE;
break;
}
lprintf("%04d !DATA ERROR sent %d instead of %d on socket %d"
,xfer.ctrl_sock,wr,rd,*xfer.data_sock);
sockprintf(xfer.ctrl_sock,"451 Short DATA transfer");
error=TRUE;
break;
}
total+=wr;
*xfer.lastactive=time(NULL);
if((i=ferror(fp))!=0)
lprintf("%04d !FILE ERROR %d (%d)",xfer.ctrl_sock,i,errno);
close_socket(xfer.data_sock,__LINE__); /* Signal end of file */
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf("%04d DATA socket closed",xfer.ctrl_sock);
if(!error) {
dur=time(NULL)-start;
cps=dur ? total/dur : total*2;
lprintf("%04d Transfer successful: %lu bytes sent in %lu seconds (%lu cps)"
,xfer.ctrl_sock
,total,dur,cps);
sockprintf(xfer.ctrl_sock,"226 Download complete (%lu cps).",cps);
if(xfer.dir>=0) {
memset(&f,0,sizeof(f));
GetShortPathName(xfer.filename,fname,sizeof(fname));
strcpy(fname,xfer.filename);
#endif
padfname(getfname(fname),f.name);
strupr(f.name);
f.dir=xfer.dir;
f.size=total;
if(getfileixb(&scfg,&f)==TRUE && getfiledat(&scfg,&f)==TRUE) {
f.timesdled++;
putfiledat(&scfg,&f);
lprintf("%04d %s downloaded: %s (%lu times total)"
,xfer.ctrl_sock
,xfer.user->alias
,xfer.filename
,f.timesdled);
}
/* Need to update datedled in index */
}
if(xfer.credits) {
xfer.user->dls=(ushort)adjustuserrec(&scfg, xfer.user->number,U_DLS,5,1);
xfer.user->dlb=adjustuserrec(&scfg, xfer.user->number,U_DLB,10,total);
if(xfer.dir>=0 && !(scfg.dir[xfer.dir]->misc&DIR_FREE)
/* && !chk_ar(&scfg, scfg.dir[xfer.dir]->ex_ar, xfer.user) */
&& !(xfer.user->exempt&FLAG('D')))
subtract_cdt(&scfg, xfer.user, xfer.credits);
}
if(!xfer.tmpfile && !xfer.delfile)
download_stats(total);
}
fclose(fp);
*xfer.inprogress=FALSE;
if(xfer.tmpfile) {
if(!(startup->options&FTP_OPT_KEEP_TEMP_FILES))
remove(xfer.filename);
}
else if(xfer.delfile && !error)
remove(xfer.filename);
thread_down();
}
static void receive_thread(void* arg)
{
char buf[8192];
char fname[MAX_PATH+1];
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
int rd;
int file;
ulong total=0;
ulong dur;
ulong cps;
BOOL error=FALSE;
FILE* fp;
file_t f;
xfer_t xfer;
time_t now;
time_t start;
time_t last_report;
xfer=*(xfer_t*)arg;
if((fp=fopen(xfer.filename,xfer.append ? "ab" : "wb"))==NULL) {
lprintf("%04d !DATA ERROR %d opening %s",xfer.ctrl_sock,xfer.filename,errno);
close_socket(xfer.data_sock,__LINE__);
return;
}
thread_up();
*xfer.inprogress=TRUE;
*xfer.aborted=FALSE;

rswindell
committed
if(xfer.filepos || startup->options&FTP_OPT_DEBUG_DATA)
lprintf("%04d DATA socket %d receiving from offset %ld"
,xfer.ctrl_sock,*xfer.data_sock,xfer.filepos);
fseek(fp,xfer.filepos,SEEK_SET);
last_report=start=time(NULL);
while(1) {
now=time(NULL);
if(total && now>=last_report+XFER_REPORT_INTERVAL) {
lprintf("%04d Received %ld bytes of %s (%lu cps)"
,xfer.ctrl_sock,total,xfer.filename
,now-start? total/(now-start) : total*2);
last_report=now;
}
if(*xfer.aborted==TRUE) {
lprintf("%04d !DATA Transfer aborted",xfer.ctrl_sock);

rswindell
committed
/* Send NAK */
sockprintf(xfer.ctrl_sock,"426 Transfer aborted.");
error=TRUE;
break;
}
if(server_socket==INVALID_SOCKET) {
lprintf("%04d !DATA Transfer locally aborted",xfer.ctrl_sock);

rswindell
committed
/* Send NAK */
sockprintf(xfer.ctrl_sock,"426 Transfer locally aborted.");
error=TRUE;
break;
}
socket_debug[xfer.ctrl_sock]|=SOCKET_DEBUG_RECV_BUF;
rd=recv(*xfer.data_sock,buf,sizeof(buf),0);
socket_debug[xfer.ctrl_sock]&=~SOCKET_DEBUG_RECV_BUF;
if(rd<1) {
if(rd==0) { /* Socket closed */
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf("%04d DATA socket %d closed by client"
,xfer.ctrl_sock,*xfer.data_sock);
break;
}
if(rd==SOCKET_ERROR) {
lprintf("%04d Connection reset by peer, receiving on socket %d"
,xfer.ctrl_sock,*xfer.data_sock);
else if(ERROR_VALUE==ECONNABORTED)
lprintf("%04d Connection aborted by peer, receiving on socket %d"
,xfer.ctrl_sock,*xfer.data_sock);
else
lprintf("%04d !DATA ERROR %d receiving on data socket %d"
,xfer.ctrl_sock,ERROR_VALUE,*xfer.data_sock);

rswindell
committed
/* Send NAK */
sockprintf(xfer.ctrl_sock,"426 Error %d receiving on DATA channel"
,ERROR_VALUE);
error=TRUE;
break;
}
lprintf("%04d !DATA ERROR recv returned %d on socket %d"
,xfer.ctrl_sock,rd,*xfer.data_sock);

rswindell
committed
/* Send NAK */
sockprintf(xfer.ctrl_sock,"451 Unexpected socket error: %d",rd);
error=TRUE;
break;
}
fwrite(buf,1,rd,fp);
total+=rd;
*xfer.lastactive=time(NULL);

rswindell
committed
*xfer.inprogress=FALSE;
fclose(fp);
close_socket(xfer.data_sock,__LINE__);
if(error && startup->options&FTP_OPT_DEBUG_DATA)
lprintf("%04d DATA socket %d closed",xfer.ctrl_sock,*xfer.data_sock);
if(!error) {
dur=time(NULL)-start;
cps=dur ? total/dur : total*2;
lprintf("%04d Transfer successful: %lu bytes received in %lu seconds (%lu cps)"
,xfer.ctrl_sock
,total,dur,cps);
if(xfer.dir>=0) {
memset(&f,0,sizeof(f));
GetShortPathName(xfer.filename,fname,sizeof(fname));
#else
strcpy(fname,xfer.filename);
#endif
padfname(getfname(fname),f.name);
strupr(f.name);
f.dir=xfer.dir;
if(scfg.dir[f.dir]->misc&DIR_AONLY) /* Forced anonymous */
f.misc|=FM_ANON;
f.cdt=total;
f.dateuled=time(NULL);
f.timesdled=0;
f.datedled=0L;
f.opencount=0;
if(xfer.desc==NULL || *xfer.desc==0) {
sprintf(f.desc,"%.*s",(int)sizeof(f.desc)-1,getfname(xfer.filename));
/* old way strcpy(f.desc,"Received via FTP: No description given"); */
} else
sprintf(f.desc,"%.*s",(int)sizeof(f.desc)-1,xfer.desc);
strcpy(f.uler,xfer.user->alias);
if(!addfiledat(&scfg,&f))
lprintf("%04d !ERROR adding file (%s) to database",xfer.ctrl_sock,f.name);
if(scfg.dir[f.dir]->upload_sem[0])

rswindell
committed
if((file=sopen(scfg.dir[f.dir]->upload_sem,O_WRONLY|O_CREAT|O_TRUNC,SH_DENYNO))!=-1)
close(file);
/**************************/
/* Update Uploader's Info */
/**************************/
xfer.user->uls=(short)adjustuserrec(&scfg, xfer.user->number,U_ULS,5,1);
xfer.user->ulb=adjustuserrec(&scfg, xfer.user->number,U_ULB,10,total);
if(scfg.dir[f.dir]->up_pct && scfg.dir[f.dir]->misc&DIR_CDTUL) { /* credit for upload */
if(scfg.dir[f.dir]->misc&DIR_CDTMIN && cps) /* Give min instead of cdt */
xfer.user->min=adjustuserrec(&scfg,xfer.user->number,U_MIN,10
,((ulong)(total*(scfg.dir[f.dir]->up_pct/100.0))/cps)/60);
else
xfer.user->cdt=adjustuserrec(&scfg,xfer.user->number,U_CDT,10
,(ulong)(f.cdt*(scfg.dir[f.dir]->up_pct/100.0)));
}
upload_stats(total);
}

rswindell
committed
/* Send ACK */
sockprintf(xfer.ctrl_sock,"226 Upload complete (%lu cps).",cps);
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
}
thread_down();
}
static void filexfer(SOCKADDR_IN* addr, SOCKET ctrl_sock, SOCKET pasv_sock, SOCKET* data_sock
,char* filename, long filepos, BOOL* inprogress, BOOL* aborted
,BOOL delfile, BOOL tmpfile
,time_t* lastactive
,user_t* user
,int dir
,BOOL receiving
,BOOL credits
,BOOL append
,char* desc)
{
int result;
int addr_len;
SOCKADDR_IN server_addr;
static xfer_t xfer;
struct timeval tv;
fd_set socket_set;
if((*inprogress)==TRUE) {
lprintf("%04d !TRANSFER already in progress",ctrl_sock);
sockprintf(ctrl_sock,"425 Transfer already in progress.");
return;
}
*inprogress=TRUE;
if(*data_sock!=INVALID_SOCKET)
close_socket(data_sock,__LINE__);
if(pasv_sock==INVALID_SOCKET) { /* !PASV */
if((*data_sock=socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) == INVALID_SOCKET) {
lprintf("%04d !DATA ERROR %d opening socket", ctrl_sock, ERROR_VALUE);
sockprintf(ctrl_sock,"425 Error %d opening socket",ERROR_VALUE);
if(tmpfile)
remove(filename);
*inprogress=FALSE;
return;
}
if(startup->socket_open!=NULL)
startup->socket_open(TRUE);
sockets++;
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf("%04d DATA socket %d opened",ctrl_sock,*data_sock);
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(0); /* 20? */
if((result=bind(*data_sock, (struct sockaddr *) &server_addr
,sizeof(server_addr)))!=0) {
lprintf ("%04d !DATA ERROR %d (%d) binding socket %d"
,ctrl_sock, result, ERROR_VALUE, *data_sock);
sockprintf(ctrl_sock,"425 Error %d binding socket",ERROR_VALUE);
if(tmpfile)
remove(filename);
*inprogress=FALSE;
socket_debug[ctrl_sock]|=SOCKET_DEBUG_CONNECT;
result=connect(*data_sock, (struct sockaddr *)addr,sizeof(struct sockaddr));
socket_debug[ctrl_sock]&=~SOCKET_DEBUG_CONNECT;
if(result!=0) {
lprintf("%04d !DATA ERROR %d (%d) connecting to client %s port %d on socket %d"
,ctrl_sock,result,ERROR_VALUE
,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port),*data_sock);
sockprintf(ctrl_sock,"425 Error %d connecting to socket",ERROR_VALUE);
if(tmpfile)
remove(filename);
*inprogress=FALSE;
return;
}
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf("%04d DATA socket %d connected to %s port %d"
,ctrl_sock,*data_sock,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port));
} else { /* PASV */
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf("%04d PASV DATA socket %d listening on %s port %d"
,ctrl_sock,pasv_sock,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port));
/* Setup for select() */
tv.tv_sec=TIMEOUT_SOCKET_LISTEN;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(pasv_sock,&socket_set);
socket_debug[ctrl_sock]|=SOCKET_DEBUG_SELECT;
result=select(pasv_sock+1,&socket_set,NULL,NULL,&tv);
socket_debug[ctrl_sock]&=~SOCKET_DEBUG_SELECT;
if(result<1) {
lprintf("%04d !PASV select returned %d (error: %d)",ctrl_sock,result,ERROR_VALUE);
sockprintf(ctrl_sock,"425 Error %d selecting socket for connection",ERROR_VALUE);
if(tmpfile)
remove(filename);
*inprogress=FALSE;
return;
}
addr_len=sizeof(SOCKADDR_IN);
socket_debug[ctrl_sock]|=SOCKET_DEBUG_ACCEPT;
*data_sock=accept(pasv_sock,(struct sockaddr*)addr,&addr_len);
socket_debug[ctrl_sock]&=~SOCKET_DEBUG_ACCEPT;
if(*data_sock==INVALID_SOCKET) {
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
lprintf("%04d !PASV DATA ERROR %d accepting connection on socket %d"
,ctrl_sock,ERROR_VALUE,pasv_sock);
sockprintf(ctrl_sock,"425 Error %d accepting connection",ERROR_VALUE);
if(tmpfile)
remove(filename);
*inprogress=FALSE;
return;
}
if(startup->socket_open!=NULL)
startup->socket_open(TRUE);
sockets++;
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf("%04d PASV DATA socket %d connected to %s port %d"
,ctrl_sock,*data_sock,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port));
}
/* Get Mutex here? */
memset(&xfer,0,sizeof(xfer));
xfer.ctrl_sock=ctrl_sock;
xfer.data_sock=data_sock;
xfer.inprogress=inprogress;
xfer.aborted=aborted;
xfer.delfile=delfile;
xfer.tmpfile=tmpfile;
xfer.append=append;
xfer.filepos=filepos;
xfer.credits=credits;
xfer.lastactive=lastactive;
xfer.user=user;
xfer.dir=dir;
xfer.desc=desc;
sprintf(xfer.filename,"%.*s",(int)sizeof(xfer.filename)-1,filename);
if(receiving)
_beginthread(receive_thread,0,(void*)&xfer);
else
_beginthread(send_thread,0,(void*)&xfer);
}
/* convert "user name" to "user.name" or "mr. user" to "mr._user" */
char* dotname(char* in, char* out)
{
char ch;
int i;
if(strchr(in,'.')==NULL)
ch='.';
else
ch='_';
for(i=0;in[i];i++)
if(in[i]<=' ')
else
out[i]=in[i];
out[i]=0;
return(out);
}
void parsepath(char** pp, user_t* user, int* curlib, int* curdir)
{
char* p;
char* tp;
char path[MAX_PATH+1];
int dir=*curdir;
int lib=*curlib;
sprintf(path,"%.*s",(int)sizeof(path)-1,*pp);
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
p=path;
if(*p=='/') {
p++;
lib=-1;
}
else if(!strncmp(p,"./",2))
p+=2;
if(!strncmp(p,"..",2)) {
p+=2;
if(dir>=0)
dir=-1;
else if(lib>=0)
lib=-1;
if(*p=='/')
p++;
}
if(*p==0) {
*curlib=lib;
*curdir=dir;
return;
}
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
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) { /* not found */
*curlib=-1;
return;
}
*curlib=lib;
if(tp==NULL) {
*curdir=-1;
return;
}
p=tp+1;
}
tp=strchr(p,'/');
if(tp!=NULL) {
*tp=0;
tp++;
} else
tp=p+strlen(p);
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;
if(!stricmp(scfg.dir[dir]->code,p))
break;
}
if(dir>=scfg.total_dirs) /* not found */
return;
*curdir=dir;
*pp+=tp-path; /* skip "lib/dir/" */
}
BOOL alias(char* fullalias, char* filename, user_t* user, int* curdir)
{
char* p;
char* tp;
char* fname="";
char line[512];
char alias[512];
char aliasfile[MAX_PATH+1];
int dir=-1;
FILE* fp;
BOOL result=FALSE;

rswindell
committed
sprintf(aliasfile,"%sftpalias.cfg",scfg.ctrl_dir);
if((fp=fopen(aliasfile,"r"))==NULL)
return(result);
sprintf(alias,"%.*s",(int)sizeof(alias)-1,fullalias);
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
p=strrchr(alias+1,'/');
if(p) {
*p=0;
fname=p+1;
}
while(!feof(fp)) {
if(!fgets(line,sizeof(line)-1,fp))
break;
p=line; /* alias */
while(*p && *p<=' ') p++;
if(*p==';') /* comment */
continue;
tp=p; /* terminator */
while(*tp && *tp>' ') tp++;
if(*tp) *tp=0;
if(stricmp(p,alias)) /* Not a match */
continue;
p=tp+1; /* filename */
while(*p && *p<=' ') p++;
tp=p; /* terminator */
while(*tp && *tp>' ') tp++;
if(*tp) *tp=0;
if(!strnicmp(p,BBS_VIRTUAL_PATH,strlen(BBS_VIRTUAL_PATH))) {
if((dir=getdir(p+strlen(BBS_VIRTUAL_PATH),user))<0) {
lprintf("0000 !Invalid virtual path (%s) for %s",p,user->alias);
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
/* invalid or no access */
continue;
}
p=strrchr(p,'/');
if(p!=NULL) p++;
if(p!=NULL && filename!=NULL) {
if(*p)
sprintf(filename,"%s%s",scfg.dir[dir]->path,p);
else
sprintf(filename,"%s%s",scfg.dir[dir]->path,fname);
}
} else if(filename!=NULL)
strcpy(filename,p);
result=TRUE; /* success */
break;
}
fclose(fp);
if(curdir!=NULL)
*curdir=dir;
return(result);
}
char* root_dir(char* path)
{
char* p;
static char root[MAX_PATH+1];
sprintf(root,"%.*s",(int)sizeof(root)-1,path);
if(!strncmp(root,"\\\\",2)) { /* network path */
p=strchr(root+2,'\\');
if(p) p=strchr(p+1,'\\');
if(p) *(p+1)=0; /* truncate at \\computer\sharename\ */
}
else if(!strncmp(root+1,":/",2) || !strncmp(root+1,":\\",2))
root[3]=0;
else if(*root=='/' || *root=='\\')
root[1]=0;
return(root);
}
char* vpath(int lib, int dir, char* str)
{
strcpy(str,"/");
if(lib<0)
return(str);
strcat(str,scfg.lib[lib]->sname);
if(dir<0)
return(str);
strcat(str,"/");
strcat(str,scfg.dir[dir]->code);
return(str);
}
static void ctrl_thread(void* arg)
{
char buf[512];
char str[128];
char* cmd;
char* p;
char* np;
char* tp;
char password[64];
char fname[MAX_PATH+1];
char qwkfile[MAX_PATH+1];
char aliasfile[MAX_PATH+1];
char aliasline[512];
char desc[501]="";
char sys_pass[128];
char* host_name;
char host_ip[64];
char path[MAX_PATH+1];
char local_dir[MAX_PATH+1];
char ren_from[MAX_PATH+1]="";
char html_index_ext[MAX_PATH+1];
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
WORD port;
ulong ip_addr;
int addr_len;
DWORD h1,h2,h3,h4;
u_short p1,p2; /* For PORT command */
int i;
int rd;
int file;
int result;
int lib;
int dir;
int curlib=-1;
int curdir=-1;
int orglib;
int orgdir;
long filepos=0L;
long timeleft;
ulong l;
ulong avail;
BOOL detail;
BOOL success;
BOOL getdate;
BOOL getsize;

rswindell
committed
BOOL delecmd;
BOOL delfile;
BOOL tmpfile;
BOOL credits;
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
BOOL transfer_inprogress;
BOOL transfer_aborted;
BOOL sysop=FALSE;
BOOL local_fsys=FALSE;
BOOL alias_dir;
FILE* fp;
FILE* alias_fp;
SOCKET sock;
SOCKET pasv_sock=INVALID_SOCKET;
SOCKET data_sock=INVALID_SOCKET;
HOSTENT* host;
SOCKADDR_IN addr;
SOCKADDR_IN data_addr;
SOCKADDR_IN pasv_addr;
ftp_t ftp=*(ftp_t*)arg;
user_t user;
time_t t;
time_t now;
time_t logintime;
time_t lastactive;
file_t f;

rswindell
committed
glob_t g;
node_t node;
client_t client;
struct tm tm;
struct tm * tm_p;
struct tm cur_tm;
JSContext* js_cx;
JSObject* js_glob;
thread_up();
lastactive=time(NULL);
sock=ftp.socket;
data_addr=ftp.client_addr;
/* Default data port is ctrl port-1 */
data_addr.sin_port=ntohs(data_addr.sin_port)-1;
data_addr.sin_port=htons(data_addr.sin_port);
lprintf("%04d CTRL thread started", sock);
free(arg);
if(startup->answer_sound[0] && !(startup->options&FTP_OPT_MUTE))
PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);
#ifdef JAVASCRIPT
if(((js_cx=js_initcx(&js_glob))==NULL)) {
lprintf("%04d !ERROR initializing JavaScript context",sock);
sockprintf(sock,"425 Error initializing JavaScript context");
close_socket(&sock,__LINE__);
thread_down();
return;
}
#endif

rswindell
committed
transfer_inprogress = FALSE;
transfer_aborted = FALSE;
l=1;
if((i=ioctlsocket(sock, FIONBIO, &l))!=0) {
lprintf("%04d !ERROR %d (%d) disabling socket blocking"
,sock, i, ERROR_VALUE);
sockprintf(sock,"425 Error %d disabling socket blocking"
,ERROR_VALUE);
close_socket(&sock,__LINE__);
thread_down();
return;
}
memset(&user,0,sizeof(user));
strcpy(host_ip,inet_ntoa(ftp.client_addr.sin_addr));
lprintf ("%04d CTRL connection accepted from: %s port %u"
,sock, host_ip, ntohs(ftp.client_addr.sin_port));
if(startup->options&FTP_OPT_NO_HOST_LOOKUP)
host=NULL;
else
host=gethostbyaddr ((char *)&ftp.client_addr.sin_addr
,sizeof(ftp.client_addr.sin_addr),AF_INET);
if(host!=NULL && host->h_name!=NULL)
host_name=host->h_name;
else
host_name="<no name>";
lprintf("%04d Host name: %s", sock, host_name);
if(trashcan(&scfg,host_ip,"ip")) {
lprintf("%04d !Client blocked in ip.can: %s", sock, host_ip);
sockprintf(sock,"550 Access denied.");
close_socket(&sock,__LINE__);
thread_down();
return;
}
if(trashcan(&scfg,host_name,"host")) {
lprintf("%04d !Client blocked in host.can: %s", sock, host_name);
sockprintf(sock,"550 Access denied.");
close_socket(&sock,__LINE__);
thread_down();
return;
}
/* For PASV mode */
addr_len=sizeof(pasv_addr);
if((result=getsockname(sock, (struct sockaddr *)&pasv_addr,&addr_len))!=0) {
lprintf("%04d !ERROR %d (%d) getting address/port", sock, result, ERROR_VALUE);
sockprintf(sock,"425 Error %d getting address/port",ERROR_VALUE);
close_socket(&sock,__LINE__);
thread_down();
return;
}
active_clients++;
update_clients();
/* Initialize client display */
client.size=sizeof(client);
client.time=time(NULL);
sprintf(client.addr,"%.*s",(int)sizeof(client.addr)-1,host_ip);
sprintf(client.host,"%.*s",(int)sizeof(client.host)-1,host_name);
client.port=ntohs(ftp.client_addr.sin_port);
client.protocol="FTP";
client.user="<unknown>";
client_on(sock,&client);
sockprintf(sock,"220-%s (%s)",scfg.sys_name, scfg.sys_inetaddr);
sockprintf(sock," Synchronet FTP Server for %s v%s Ready"
,PLATFORM_DESC,FTP_VERSION);

rswindell
committed
sprintf(str,"%sftplogin.txt",scfg.text_dir);
if((fp=fopen(str,"rb"))!=NULL) {
while(!feof(fp)) {
if(!fgets(buf,sizeof(buf),fp))
break;
truncsp(buf);
sockprintf(sock," %s",buf);
}
fclose(fp);
}
sockprintf(sock,"220 Please enter your user name.");
socket_debug[sock]|=SOCKET_DEBUG_CTRL;
socket_debug[sock]|=SOCKET_DEBUG_READLINE;
rd = sockreadline(sock, buf, sizeof(buf), &lastactive);
socket_debug[sock]&=~SOCKET_DEBUG_READLINE;
if(rd<1) {
if(transfer_inprogress==TRUE)
transfer_aborted=TRUE;
break;
}
truncsp(buf);
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
lastactive=time(NULL);
cmd=buf;
while(((BYTE)*cmd)==TELNET_IAC) {
cmd++;
lprintf("%04d RX: Telnet cmd %d",sock,(BYTE)*cmd);
cmd++;
}
while(*cmd && *cmd<' ') {
lprintf("%04d RX: %d (0x%02X)",sock,(BYTE)*cmd,(BYTE)*cmd);
cmd++;
}
if(!(*cmd))
continue;
if(startup->options&FTP_OPT_DEBUG_RX)
lprintf("%04d RX: %s", sock, cmd);
if(!stricmp(cmd, "NOOP")) {
sockprintf(sock,"200 NOOP command successful.");
continue;
}
if(!stricmp(cmd, "HELP SITE") || !stricmp(cmd, "SITE HELP")) {
sockprintf(sock,"214-The following SITE commands are recognized (* => unimplemented):");
sockprintf(sock," HELP WHO");
sockprintf(sock,"214 Direct comments to sysop@%s.",scfg.sys_inetaddr);
continue;
}
if(!strnicmp(cmd, "HELP",4)) {
sockprintf(sock,"214-The following commands are recognized (* => unimplemented, # => extension):");
sockprintf(sock," USER PASS CWD XCWD CDUP XCUP PWD XPWD");
sockprintf(sock," QUIT REIN PORT PASV LIST NLST NOOP HELP");
sockprintf(sock," SIZE MDTM RETR STOR REST ALLO ABOR SYST");
sockprintf(sock," TYPE STRU MODE SITE RNFR* RNTO* DELE* DESC#");
sockprintf(sock," FEAT# OPTS#");
sockprintf(sock,"214 Direct comments to sysop@%s.",scfg.sys_inetaddr);
continue;
}
if(!stricmp(cmd, "FEAT")) {
sockprintf(sock,"211-The following additional (post-RFC949) features are supported:");
sockprintf(sock," DESC");
sockprintf(sock," MDTM");
sockprintf(sock," SIZE");
sockprintf(sock," REST STREAM");
sockprintf(sock,"211 End");
continue;
}
if(!strnicmp(cmd, "OPTS",4)) {
sockprintf(sock,"501 No options supported.");
continue;
}
if(!stricmp(cmd, "QUIT")) {

rswindell
committed
sprintf(str,"%sftpbye.txt",scfg.text_dir);
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
if((fp=fopen(str,"rb"))!=NULL) {
i=0;
while(!feof(fp)) {
if(!fgets(buf,sizeof(buf),fp))
break;
truncsp(buf);
if(!i)
sockprintf(sock,"221-%s",buf);
else
sockprintf(sock," %s",buf);
i++;
}
fclose(fp);
}
sockprintf(sock,"221 Goodbye. Closing control connection.");
break;
}
if(!strnicmp(cmd, "USER ",5)) {
sysop=FALSE;
user.number=0;
p=cmd+5;
while(*p && *p<=' ') p++;
truncsp(p);
sprintf(user.alias,"%.*s",(int)sizeof(user.alias)-1,p);
if(!stricmp(user.alias,"anonymous"))
strcpy(user.alias,"guest");
user.number=matchuser(&scfg,user.alias);
if(user.number && getuserdat(&scfg, &user)==0 && user.pass[0]==0)
sockprintf(sock,"331 User name okay, give your full e-mail address as password.");
else
sockprintf(sock,"331 User name okay, need password.");
continue;
}
if(!strnicmp(cmd, "PASS ",5) && user.alias[0]) {
user.number=0;
p=cmd+5;
while(*p && *p<=' ') p++;
sprintf(password,"%.*s",(int)sizeof(password)-1,p);
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
user.number=matchuser(&scfg,user.alias);
if(!user.number) {
lprintf("%04d !FTP: UNKNOWN USER: %s",sock,user.alias);
sockprintf(sock,"530 Password not accepted.");
continue;
}
if((i=getuserdat(&scfg, &user))!=0) {
lprintf("%04d !FTP: ERROR %d getting data on user #%d (%s)"
,sock,i,user.number,user.alias);
sockprintf(sock,"530 Database error %d",i);
user.number=0;
continue;
}
if(user.misc&(DELETED|INACTIVE)) {
lprintf("%04d !FTP: DELETED or INACTIVE user #%d (%s)"
,sock,user.number,user.alias);
sockprintf(sock,"530 Password not accepted.");
user.number=0;
continue;
}
if(user.rest&FLAG('T')) {
lprintf("%04d !FTP: 'T' RESTRICTED user #%d (%s)"
,sock,user.number,user.alias);
sockprintf(sock,"530 Password not accepted.");
user.number=0;
continue;
}
if(user.ltoday>scfg.level_callsperday[user.level]
&& !(user.exempt&FLAG('L'))) {
lprintf("%04d !FTP: Max logons (%d) reached"
,sock,scfg.level_callsperday[user.level]);
sockprintf(sock,"530 Maximum logons reached.");
user.number=0;
continue;
}
if(user.rest&FLAG('L') && user.ltoday>1) {
lprintf("%04d !FTP: L Restricted user already on today"
,sock);
sockprintf(sock,"530 Maximum logons reached.");
user.number=0;
continue;
}
sprintf(sys_pass,"%s:%s",user.pass,scfg.sys_pass);
if(!user.pass[0]) { /* Guest/Anonymous */
lprintf("%04d Guest: %s",sock,password);
putuserrec(&scfg,user.number,U_NETMAIL,LEN_NETMAIL,password);
}
else if(user.level>=SYSOP_LEVEL && !stricmp(password,sys_pass)) {
lprintf("%04d Sysop access granted to %s", sock, user.alias);
sysop=TRUE;
}
else if(stricmp(password,user.pass)) {
lprintf("%04d !FTP: WRONG PASSWORD for user %s: '%s' expected '%s'"
,sock,user.alias,password,user.pass);
sockprintf(sock,"530 Password not accepted.");
user.number=0;
continue;
}
/* Update client display */
client.user=user.alias;
client_on(sock,&client);
lprintf("%04d %s logged in.",sock,user.alias);
logintime=time(NULL);
timeleft=gettimeleft(&scfg,&user,logintime);

rswindell
committed
sprintf(str,"%sftphello.txt",scfg.text_dir);
if((fp=fopen(str,"rb"))!=NULL) {
i=0;
while(!feof(fp)) {
if(!fgets(buf,sizeof(buf),fp))
break;
truncsp(buf);
if(!i)
sockprintf(sock,"230-%s",buf);
else
sockprintf(sock," %s",buf);
i++;
}
fclose(fp);
}
#ifdef JAVASCRIPT
if((js_user=CreateUserObject(&scfg, js_cx, js_glob, "user", &user))==NULL) {
lprintf("%04d !JavaScript ERROR creating user object",sock);
}
// js_CreateLibsObject(&scfg, js_cx, js_glob, "libraries
if(sysop)
sockprintf(sock,"230-Sysop access granted.");
sockprintf(sock,"230-%s logged in.",user.alias);
if(!(user.exempt&FLAG('D')) && (user.cdt+user.freecdt)>0)
sockprintf(sock,"230-You have %lu download credits."
,user.cdt+user.freecdt);
sockprintf(sock,"230 You are allowed %ld minutes of use for this session."

rswindell
committed
sprintf(qwkfile,"%sfile/%04d.qwk",scfg.data_dir,user.number);
/* Adjust User Total Logons/Logons Today */
adjustuserrec(&scfg,user.number,U_LOGONS,5,1);
putuserrec(&scfg,user.number,U_LTODAY,5,ultoa(user.ltoday+1,str,10));
putuserrec(&scfg,user.number,U_MODEM,LEN_MODEM,"FTP");
putuserrec(&scfg,user.number,U_COMP,LEN_COMP,host_name);
putuserrec(&scfg,user.number,U_NOTE,LEN_NOTE,host_ip);
getuserdat(&scfg, &user); /* make user current */
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
continue;
}
if(!user.number) {
sockprintf(sock,"530 Please login with USER and PASS.");
continue;
}
if((timeleft=gettimeleft(&scfg,&user,logintime))<1L) {
sockprintf(sock,"421 Sorry, you've run out of time.");
lprintf("%04d Out of time, disconnecting",sock);
break;
}
/********************************/
/* These commands require login */
/********************************/
if(!stricmp(cmd, "REIN")) {
lprintf("%04d %s reinitialized control session",sock,user.alias);
user.number=0;
sysop=FALSE;
filepos=0;
sockprintf(sock,"220 Control session re-initialized. Ready for re-login.");
continue;
}
if(!stricmp(cmd, "SITE WHO")) {
sockprintf(sock,"211-Active users");
for(i=0;i<scfg.sys_nodes && i<scfg.sys_lastnode;i++) {
if((result=getnodedat(&scfg, i+1, &node, 0))!=0) {
sockprintf(sock," Error %d getting data for Telnet Node %d",result,i+1);
continue;
}
if(node.status==NODE_INUSE)
sockprintf(sock," Telnet Node %3d: %s",i+1, username(&scfg,node.useron,str));
}
sockprintf(sock,"211 End");
continue;
}
if(!stricmp(cmd, "SITE DEBUG")) {
sockprintf(sock,"211-Debug");
for(i=0;i<sizeof(socket_debug);i++)
if(socket_debug[i]!=0)
sockprintf(sock,"211-socket %d = %X",i,socket_debug[i]);
sockprintf(sock,"211 End");
continue;
}
if(!strnicmp(cmd, "PORT ",5)) {
p=cmd+5;
while(*p && *p<=' ') p++;
sscanf(p,"%ld,%ld,%ld,%ld,%hd,%hd",&h1,&h2,&h3,&h4,&p1,&p2);
data_addr.sin_addr.s_addr=htonl((h1<<24)|(h2<<16)|(h3<<8)|h4);
data_addr.sin_port=htons((u_short)((p1<<8)|p2));
sockprintf(sock,"200 PORT Command successful.");
continue;
}
if(!stricmp(cmd, "PASV")) {
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
if(pasv_sock!=INVALID_SOCKET)
close_socket(&pasv_sock,__LINE__);
if((pasv_sock=open_socket(SOCK_STREAM))==INVALID_SOCKET) {
lprintf("%04d !PASV ERROR %d opening socket", sock,ERROR_VALUE);
sockprintf(sock,"425 Error %d opening PASV data socket", ERROR_VALUE);
continue;
}
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf("%04d PASV DATA socket %d opened",sock,pasv_sock);
pasv_addr.sin_port = 0;
if((result=bind(pasv_sock, (struct sockaddr *) &pasv_addr,sizeof(pasv_addr)))!= 0) {
lprintf("%04d !PASV ERROR %d (%d) binding socket", sock, result, ERROR_VALUE);
sockprintf(sock,"425 Error %d binding data socket",ERROR_VALUE);
close_socket(&pasv_sock,__LINE__);
continue;
}
addr_len=sizeof(addr);
if((result=getsockname(pasv_sock, (struct sockaddr *)&addr,&addr_len))!=0) {
lprintf("%04d !PASV ERROR %d (%d) getting address/port", sock, result, ERROR_VALUE);
sockprintf(sock,"425 Error %d getting address/port",ERROR_VALUE);
close_socket(&pasv_sock,__LINE__);
continue;
}
if((result=listen(pasv_sock, 1))!= 0) {
lprintf("%04d !PASV ERROR %d (%d) listening on socket", sock, result, ERROR_VALUE);
sockprintf(sock,"425 Error %d listening on data socket",ERROR_VALUE);
close_socket(&pasv_sock,__LINE__);
continue;
}
ip_addr=ntohl(pasv_addr.sin_addr.s_addr);
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
port=ntohs(addr.sin_port);
sockprintf(sock,"227 Entering Passive Mode (%d,%d,%d,%d,%hd,%hd)"
,(ip_addr>>24)&0xff
,(ip_addr>>16)&0xff
,(ip_addr>>8)&0xff
,ip_addr&0xff
,(port>>8)&0xff
,port&0xff
);
continue;
}
if(!strnicmp(cmd, "TYPE ",5)) {
sockprintf(sock,"200 All files sent in BINARY mode.");
continue;
}
if(!strnicmp(cmd, "ALLO",4)) {
p=cmd+5;
while(*p && *p<=' ') p++;
if(*p)
l=atol(p);
else
l=0;
if(local_fsys)
avail=getfreediskspace(local_dir);
else
avail=getfreediskspace(scfg.data_dir); /* Change to temp_dir? */
if(l && l>avail)
sockprintf(sock,"504 Only %ld bytes available.",avail);
else
sockprintf(sock,"200 %ld bytes available.",avail);
continue;
}
if(!strnicmp(cmd, "REST",4)) {
p=cmd+4;
while(*p && *p<=' ') p++;
if(*p)
filepos=atol(p);
else
filepos=0;
sockprintf(sock,"200 File marker set to %ld",filepos);
continue;
}
if(!strnicmp(cmd, "MODE ",5)) {
p=cmd+5;
while(*p && *p<=' ') p++;
if(toupper(*p)!='S')
sockprintf(sock,"504 Only STREAM mode supported.");
else
sockprintf(sock,"200 STREAM mode.");
continue;
}
if(!strnicmp(cmd, "STRU ",5)) {
p=cmd+5;
while(*p && *p<=' ') p++;
if(toupper(*p)!='F')
sockprintf(sock,"504 Only FILE structure supported.");
else
sockprintf(sock,"200 FILE structure.");
continue;
}
if(!stricmp(cmd, "SYST")) {
sockprintf(sock,"215 UNIX Type: L8");
continue;
}
if(!stricmp(cmd, "ABOR")) {
if(!transfer_inprogress)
sockprintf(sock,"226 No tranfer in progress.");
else {
transfer_aborted=TRUE;
mswait(1); /* give send thread time to abort */
sockprintf(sock,"226 Transfer aborted.");
}
continue;
}
if(!strnicmp(cmd,"SMNT ",5) && sysop && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)) {
p=cmd+5;
while(*p && *p<=' ') p++;
if(!stricmp(p,BBS_FSYS_DIR))
local_fsys=FALSE;
else {
if(!direxist(p)) {
sockprintf(sock,"550 Directory does not exist.");
lprintf("%04d !%s attempted to mount invalid directory: %s"
,sock, user.alias, p);
continue;
}
local_fsys=TRUE;
sprintf(local_dir,"%.*s",(int)sizeof(local_dir)-1,p);
}
sockprintf(sock,"250 %s file system mounted."
,local_fsys ? "Local" : "BBS");
lprintf("%04d %s mounted %s file system"
,sock, user.alias, local_fsys ? "local" : "BBS");
continue;
}
/****************************/
/* Local File System Access */
/****************************/
if(sysop && local_fsys && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)) {
if(local_dir[0]
&& local_dir[strlen(local_dir)-1]!='\\'
&& local_dir[strlen(local_dir)-1]!='/')
strcat(local_dir,"/");
if(!strnicmp(cmd, "LIST", 4) || !strnicmp(cmd, "NLST", 4)) {
sprintf(fname,"%sftp%d.tx", scfg.data_dir, sock);
if((fp=fopen(fname,"w+b"))==NULL) {
lprintf("%04d !ERROR %d opening %s",sock,errno,fname);
sockprintf(sock, "451 Insufficient system storage");
continue;
}
if(!strnicmp(cmd, "LIST", 4))
detail=TRUE;
else
detail=FALSE;
p=cmd+4;
while(*p && *p<=' ') p++;

rswindell
committed
sprintf(path,"%s%s",local_dir, *p ? p : "*");
lprintf("%04d %s listing: %s", sock, user.alias, path);
sockprintf(sock, "150 Directory of %s%s", local_dir, p);

rswindell
committed
glob(path,0,NULL,&g);
for(i=0;i<(int)g.gl_pathc;i++) {

rswindell
committed
f.size=flength(g.gl_pathv[i]);
t=fdate(g.gl_pathv[i]);
tm_p=localtime(&t);
if(tm_p==NULL)
memset(&tm,0,sizeof(tm));
else
tm=*tm_p;
fprintf(fp,"%crw-r--r-- 1 %-8s local %9ld %s %2d "

rswindell
committed
,isdir(g.gl_pathv[i]) ? 'd':'-'
,scfg.sys_id
,f.size
,mon[tm.tm_mon],tm.tm_mday);
if(tm.tm_year==cur_tm.tm_year)
fprintf(fp,"%02d:%02d %s\r\n"
,tm.tm_hour,tm.tm_min

rswindell
committed
,getfname(g.gl_pathv[i]));
else
fprintf(fp,"%5d %s\r\n"
,1900+tm.tm_year

rswindell
committed
,getfname(g.gl_pathv[i]));

rswindell
committed
fprintf(fp,"%s\r\n",getfname(g.gl_pathv[i]));

rswindell
committed
globfree(&g);
fclose(fp);
filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,0L
,&transfer_inprogress,&transfer_aborted
,TRUE /* delfile */
,TRUE /* tmpfile */
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
,&lastactive,&user,-1,FALSE,FALSE,FALSE,NULL);
continue;
} /* Local LIST/NLST */
if(!strnicmp(cmd, "CWD ", 4) || !strnicmp(cmd,"XCWD ",5)) {
if(!strnicmp(cmd,"CWD ",4))
p=cmd+4;
else
p=cmd+5;
while(*p && *p<=' ') p++;
tp=p;
if(*tp=='/' || *tp=='\\') /* /local: and /bbs: are valid */
tp++;
if(!strnicmp(tp,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR))) {
local_fsys=FALSE;
sockprintf(sock,"250 CWD command successful (BBS file system mounted).");
lprintf("%04d %s mounted BBS file system", sock, user.alias);
continue;
}
if(!strnicmp(tp,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) {
tp+=strlen(LOCAL_FSYS_DIR); /* already mounted */
p=tp;
}
if(p[1]==':' || !strncmp(p,"\\\\",2))
strcpy(path,p);
else if(*p=='/' || *p=='\\')
sprintf(path,"%s%s",root_dir(local_dir),p);
else {
sprintf(fname,"%s%s",local_dir,p);
_fullpath(path,fname,sizeof(path));
}
if(!direxist(path)) {
sockprintf(sock,"550 Directory does not exist (%s).",path);
lprintf("%04d !%s attempted to change to an invalid directory: %s"
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
,sock, user.alias, path);
} else {
strcpy(local_dir,path);
sockprintf(sock,"250 CWD command successful (%s).", local_dir);
}
continue;
} /* Local CWD */
if(!stricmp(cmd,"CDUP") || !stricmp(cmd,"XCUP")) {
sprintf(path,"%s..",local_dir);
if(_fullpath(local_dir,path,sizeof(local_dir))==NULL)
sockprintf(sock,"550 Directory does not exist.");
else
sockprintf(sock,"200 CDUP command successful.");
continue;
}
if(!stricmp(cmd, "PWD") || !stricmp(cmd,"XPWD")) {
if(strlen(local_dir)>3)
local_dir[strlen(local_dir)-1]=0; /* truncate '/' */
sockprintf(sock,"257 \"%s\" is current directory."
,local_dir);
continue;
} /* Local PWD */
if(!strnicmp(cmd, "MKD ", 4) || !strnicmp(cmd,"XMKD",4)) {
p=cmd+4;
while(*p && *p<=' ') p++;
if(*p=='/') /* absolute */
sprintf(fname,"%s%s",root_dir(local_dir),p+1);
else /* relative */
sprintf(fname,"%s%s",local_dir,p);
if((i=_mkdir(fname))==0) {
sockprintf(sock,"257 \"%s\" directory created",fname);
lprintf("%04d %s created directory: %s",sock,user.alias,fname);
} else {
sockprintf(sock,"521 Error %d creating directory: %s",i,fname);
lprintf("%04d !%s attempted to create directory: %s (Error %d)"
,sock,user.alias,fname,i);
}
continue;
}
if(!strnicmp(cmd, "RMD ", 4) || !strnicmp(cmd,"XRMD",4)) {
p=cmd+4;
while(*p && *p<=' ') p++;
if(*p=='/') /* absolute */
sprintf(fname,"%s%s",root_dir(local_dir),p+1);
else /* relative */
sprintf(fname,"%s%s",local_dir,p);
if((i=_rmdir(fname))==0) {
sockprintf(sock,"250 \"%s\" directory removed",fname);
lprintf("%04d %s removed directory: %s",sock,user.alias,fname);
} else {
sockprintf(sock,"450 Error %d removing directory: %s",i,fname);
lprintf("%04d !%s attempted to remove directory: %s (Error %d)"
,sock,user.alias,fname,i);
}
continue;
}
if(!strnicmp(cmd, "RNFR ",5)) {
p=cmd+5;
while(*p && *p<=' ') p++;
if(*p=='/') /* absolute */
sprintf(ren_from,"%s%s",root_dir(local_dir),p+1);
else /* relative */
sprintf(ren_from,"%s%s",local_dir,p);
if(!fexist(ren_from)) {
sockprintf(sock,"550 File not found: %s",ren_from);
lprintf("%04d !%s attempted to rename %s (not found)"
,sock,user.alias,ren_from);
} else
sockprintf(sock,"350 File exists, ready for destination name");
continue;
}
if(!strnicmp(cmd, "RNTO ",5)) {
p=cmd+5;
while(*p && *p<=' ') p++;
if(*p=='/') /* absolute */
sprintf(fname,"%s%s",root_dir(local_dir),p+1);
else /* relative */
sprintf(fname,"%s%s",local_dir,p);
if((i=rename(ren_from, fname))==0) {
sockprintf(sock,"250 \"%s\" renamed to \"%s\"",ren_from,fname);
lprintf("%04d %s renamed %s to %s",sock,user.alias,ren_from,fname);
} else {
sockprintf(sock,"450 Error %d renaming file: %s",i,ren_from);
lprintf("%04d !%s attempted to rename file: %s (Error %d)"
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
,sock,user.alias,ren_from,i);
}
continue;
}
if(!strnicmp(cmd, "RETR ", 5) || !strnicmp(cmd,"SIZE ",5)
|| !strnicmp(cmd, "MDTM ",5) || !strnicmp(cmd, "DELE ",5)) {
p=cmd+5;
while(*p && *p<=' ') p++;
if(!strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR)))
p+=strlen(LOCAL_FSYS_DIR); /* already mounted */
if(p[1]==':') /* drive specified */
strcpy(fname,p);
else if(*p=='/') /* absolute, current drive */
sprintf(fname,"%s%s",root_dir(local_dir),p+1);
else /* relative */
sprintf(fname,"%s%s",local_dir,p);
if(!fexist(fname)) {
lprintf("%04d !%s file not found: %s",sock,user.alias,fname);
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
sockprintf(sock,"550 File not found: %s",fname);
continue;
}
if(!strnicmp(cmd,"SIZE ",5)) {
sockprintf(sock,"213 %lu",flength(fname));
continue;
}
if(!strnicmp(cmd,"MDTM ",5)) {
t=fdate(fname);
tm_p=gmtime(&t);
if(tm_p==NULL)
memset(&tm,0,sizeof(tm));
else
tm=*tm_p;
sockprintf(sock,"213 %u%02u%02u%02u%02u%02u"
,1900+tm.tm_year,tm.tm_mon+1,tm.tm_mday
,tm.tm_hour,tm.tm_min,tm.tm_sec);
continue;
}
if(!strnicmp(cmd,"DELE ",5)) {
if((i=remove(fname))==0) {
sockprintf(sock,"250 \"%s\" removed successfully.",fname);
lprintf("%04d %s deleted file: %s",sock,user.alias,fname);
} else {
sockprintf(sock,"450 Error %d removing file: %s",i,fname);
lprintf("%04d !%s attempted to delete file: %s (Error %d)"
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
,sock,user.alias,fname,i);
}
continue;
}
/* RETR */
lprintf("%04d %s downloading: %s (%ld bytes)"
,sock,user.alias,fname,flength(fname));
sockprintf(sock,"150 Opening BINARY mode data connection for file transfer.");
filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,filepos
,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
,&lastactive,&user,-1,FALSE,FALSE,FALSE,NULL);
continue;
} /* Local RETR/SIZE/MDTM */
if(!strnicmp(cmd, "STOR ", 5) || !strnicmp(cmd, "APPE ", 5)) {
p=cmd+5;
while(*p && *p<=' ') p++;
if(!strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR)))
p+=strlen(LOCAL_FSYS_DIR); /* already mounted */
if(p[1]==':') /* drive specified */
strcpy(fname,p);
else if(*p=='/') /* absolute, current drive */
sprintf(fname,"%s%s",root_dir(local_dir),p+1);
else /* relative */
sprintf(fname,"%s%s",local_dir,p);
lprintf("%04d %s uploading %s", sock,user.alias,fname);
sockprintf(sock,"150 Opening BINARY mode data connection for file transfer.");
filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,filepos
,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
,&lastactive
,&user
,-1 /* dir */
,TRUE /* uploading */
,FALSE /* credits */
,!strnicmp(cmd,"APPE",4) ? TRUE : FALSE /* append */
,NULL /* desc */
);
filepos=0;
continue;
} /* Local STOR */
}
if(!strnicmp(cmd, "LIST", 4) || !strnicmp(cmd, "NLST", 4)) {
dir=curdir;
lib=curlib;
if(cmd[4]!=0)
lprintf("%04d LIST/NLST: %s",sock,cmd);
/* path specified? */
p=cmd+4;
while(*p && *p<=' ') p++;
if(*p=='-') { /* -Letc */
while(*p && *p>' ') p++;
while(*p && *p<=' ') p++;
}
parsepath(&p,&user,&lib,&dir);
sprintf(fname,"%sftp%d.tx", scfg.data_dir, sock);
if((fp=fopen(fname,"w+b"))==NULL) {
lprintf("%04d !ERROR %d opening %s",sock,errno,fname);
sockprintf(sock, "451 Insufficient system storage");
continue;
}
if(!strnicmp(cmd, "LIST", 4))
detail=TRUE;
else
detail=FALSE;
sockprintf(sock,"150 Opening ASCII mode data connection for /bin/ls.");
now=time(NULL);
tm_p=localtime(&now);
if(tm_p==NULL)
memset(&cur_tm,0,sizeof(cur_tm));
else
cur_tm=*tm_p;
if(startup->options&FTP_OPT_INDEX_FILE && startup->index_file_name[0]
&& (!stricmp(p,startup->index_file_name) || *p==0 || *p=='*')) {
if(detail)
fprintf(fp,"-rw-r--r-- 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
,NAME_LEN
,scfg.sys_id
,lib<0 ? scfg.sys_id : dir<0
? scfg.lib[lib]->sname : scfg.dir[dir]->code
,mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
,startup->index_file_name);
else
fprintf(fp,"%s\r\n",startup->index_file_name);
}
/* HTML Index File */
if(startup->options&FTP_OPT_HTML_INDEX_FILE && startup->html_index_file[0]
&& (!stricmp(p,startup->html_index_file) || *p==0 || *p=='*')) {
if(detail)
fprintf(fp,"-rw-r--r-- 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
,NAME_LEN
,scfg.sys_id
,lib<0 ? scfg.sys_id : dir<0
? scfg.lib[lib]->sname : scfg.dir[dir]->code
,512L
,mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
,startup->html_index_file);
else
fprintf(fp,"%s\r\n",startup->html_index_file);
}
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
if(lib<0) { /* Root dir */
lprintf("%04d %s listing: root",sock,user.alias);
/* QWK Packet */
if(startup->options&FTP_OPT_ALLOW_QWK/* && fexist(qwkfile)*/) {
if(detail) {
if(fexist(qwkfile)) {
t=fdate(qwkfile);
l=flength(qwkfile);
} else {
t=time(NULL);
l=10240;
};
tm_p=localtime(&t);
if(tm_p==NULL)
memset(&tm,0,sizeof(tm));
else
tm=*tm_p;
fprintf(fp,"-rw-r--r-- 1 %-*s %-8s %9ld %s %2d %02d:%02d %s.qwk\r\n"
,NAME_LEN
,scfg.sys_id
,scfg.sys_id
,l
,mon[tm.tm_mon],tm.tm_mday,tm.tm_hour,tm.tm_min
,scfg.sys_id);
} else
fprintf(fp,"%s.qwk\r\n",scfg.sys_id);
}
/* File Aliases */

rswindell
committed
sprintf(aliasfile,"%sftpalias.cfg",scfg.ctrl_dir);
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
if((alias_fp=fopen(aliasfile,"r"))!=NULL) {
while(!feof(alias_fp)) {
if(!fgets(aliasline,sizeof(aliasline)-1,alias_fp))
break;
alias_dir=FALSE;
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++;
tp=np; /* terminator pointer */
while(*tp && *tp>' ') tp++;
if(*tp) *tp=0;
/* 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) {
sprintf(aliasfile,"%s%s",scfg.dir[dir]->path,tp);
np=aliasfile;
}
else
alias_dir=TRUE;
}
if(!alias_dir && !fexist(np))
continue;
if(detail) {
if(alias_dir==TRUE) {
fprintf(fp,"drwxr-xr-x 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
,NAME_LEN
,scfg.sys_id
,scfg.lib[scfg.dir[dir]->lib]->sname
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
,mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
,p);
}
else {
t=fdate(np);
tm_p=localtime(&t);
if(tm_p==NULL)
memset(&tm,0,sizeof(tm));
else
tm=*tm_p;
fprintf(fp,"-rw-r--r-- 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
,NAME_LEN
,scfg.sys_id
,scfg.sys_id
,flength(np)
,mon[tm.tm_mon],tm.tm_mday,tm.tm_hour,tm.tm_min
,p);
}
} else
fprintf(fp,"%s\r\n",p);
}
fclose(alias_fp);
}
/* Library folders */
for(i=0;i<scfg.total_libs;i++) {
if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
continue;
if(detail)
fprintf(fp,"drwxr-xr-x 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
,NAME_LEN
,scfg.sys_id
,scfg.sys_id
,mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
,scfg.lib[i]->sname);
else
fprintf(fp,"%s\r\n",scfg.lib[i]->sname);
}
} else if(dir<0) {
lprintf("%04d %s listing: %s library",sock,user.alias,scfg.lib[lib]->sname);
for(i=0;i<scfg.total_dirs;i++) {
if(scfg.dir[i]->lib!=lib)
continue;
if(i!=scfg.sysop_dir && i!=scfg.upload_dir
&& !chk_ar(&scfg,scfg.dir[i]->ar,&user))
continue;
if(detail)
fprintf(fp,"drwxr-xr-x 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
,NAME_LEN
,scfg.sys_id
,scfg.lib[lib]->sname
,mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
,scfg.dir[i]->code);
else
fprintf(fp,"%s\r\n",scfg.dir[i]->code);
}
} else if(chk_ar(&scfg,scfg.dir[dir]->ar,&user)) {
lprintf("%04d %s listing: %s/%s directory"
,sock,user.alias,scfg.lib[lib]->sname,scfg.dir[dir]->code);
sprintf(path,"%s%s",scfg.dir[dir]->path,*p ? p : "*");

rswindell
committed
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
strcpy(str,g.gl_pathv[i]);
#endif
padfname(getfname(str),f.name);
strupr(f.name);
f.dir=dir;

rswindell
committed
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
if((filedat=getfileixb(&scfg,&f))==FALSE
&& !(startup->options&FTP_OPT_DIR_FILES))
continue;
if(detail) {
f.size=flength(g.gl_pathv[i]);
getfiledat(&scfg,&f);
t=fdate(g.gl_pathv[i]);
tm_p=localtime(&t);
if(tm_p==NULL)
memset(&tm,0,sizeof(tm));
else
tm=*tm_p;
fprintf(fp,"-rw-r--r-- 1 %-*s %-8s %9ld %s %2d "
,NAME_LEN
,filedat ? dotname(f.uler,str) : scfg.sys_id
,scfg.dir[dir]->code
,f.size
,mon[tm.tm_mon],tm.tm_mday);
if(tm.tm_year==cur_tm.tm_year)
fprintf(fp,"%02d:%02d %s\r\n"
,tm.tm_hour,tm.tm_min
,getfname(g.gl_pathv[i]));
else
fprintf(fp,"%5d %s\r\n"
,1900+tm.tm_year
,getfname(g.gl_pathv[i]));
} else
fprintf(fp,"%s\r\n",getfname(g.gl_pathv[i]));

rswindell
committed
globfree(&g);

rswindell
committed
lprintf("%04d %s listing: %s/%s directory (empty - no access)"
,sock,user.alias,scfg.lib[lib]->sname,scfg.dir[dir]->code);
fclose(fp);
filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,0L
,&transfer_inprogress,&transfer_aborted
,TRUE /* delfile */
,TRUE /* tmpfile */
,&lastactive,&user,dir,FALSE,FALSE,FALSE,NULL);
continue;
}

rswindell
committed
if(!strnicmp(cmd, "RETR ", 5)
|| !strnicmp(cmd, "SIZE ",5)
|| !strnicmp(cmd, "MDTM ",5)
|| !strnicmp(cmd, "DELE ",5)) {
getdate=FALSE;
getsize=FALSE;

rswindell
committed
delecmd=FALSE;
if(!strnicmp(cmd,"SIZE ",5))
getsize=TRUE;
else if(!strnicmp(cmd,"MDTM ",5))
getdate=TRUE;

rswindell
committed
else if(!strnicmp(cmd,"DELE ",5))
delecmd=TRUE;
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
if(!getsize && !getdate && user.rest&FLAG('D')) {
sockprintf(sock,"550 Insufficient access.");
filepos=0;
continue;
}
credits=TRUE;
success=FALSE;
delfile=FALSE;
tmpfile=FALSE;
lib=curlib;
dir=curdir;
p=cmd+5;
while(*p && *p<=' ') p++;
if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR)))
p+=strlen(BBS_FSYS_DIR); /* already mounted */
if(*p=='/') {
lib=-1;
p++;
}
else if(!strncmp(p,"./",2))
p+=2;
if(lib<0 && alias(p, fname, &user, &dir)==TRUE) {
success=TRUE;
credits=TRUE; /* include in d/l stats */
tmpfile=FALSE;
delfile=FALSE;
lprintf("%04d %s %.4s by alias: %s"
,sock,user.alias,cmd,p);
if(dir>=0)
lib=scfg.dir[dir]->lib;
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
}
if(!success && lib<0 && (tp=strchr(p,'/'))!=NULL) {
dir=-1;
*tp=0;
for(i=0;i<scfg.total_libs;i++) {
if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
continue;
if(!stricmp(scfg.lib[i]->sname,p))
break;
}
if(i<scfg.total_libs)
lib=i;
p=tp+1;
}
if(!success && dir<0 && (tp=strchr(p,'/'))!=NULL) {
*tp=0;
for(i=0;i<scfg.total_dirs;i++) {
if(scfg.dir[i]->lib!=lib)
continue;
if(!chk_ar(&scfg,scfg.dir[i]->ar,&user))
continue;
if(!stricmp(scfg.dir[i]->code,p))
break;
}
if(i<scfg.total_dirs)
dir=i;
p=tp+1;
}
sprintf(html_index_ext,"%s?",startup->html_index_file);
sprintf(str,"%s.qwk",scfg.sys_id);
if(lib<0 && startup->options&FTP_OPT_ALLOW_QWK

rswindell
committed
&& !stricmp(p,str) && !delecmd) {
lprintf("%04d %s creating/updating QWK packet...",sock,user.alias);

rswindell
committed
sprintf(str,"%spack%04u.now",scfg.data_dir,user.number);
if((file=open(str,O_WRONLY|O_CREAT,S_IWRITE))==-1) {
lprintf("%04d !ERROR %d opening %s",sock, errno, str);
sockprintf(sock, "451 !ERROR %d creating semaphore file",errno);
filepos=0;
continue;
}
close(file);
t=time(NULL);
while(fexist(str)) {
if(time(NULL)-t>300)
break;
}
if(fexist(str)) {
lprintf("%04d !TIMEOUT waiting for QWK packet creation",sock);
sockprintf(sock,"451 Time-out waiting for packet creation.");
remove(str);
filepos=0;
continue;
}
if(!fexist(qwkfile)) {
lprintf("%04d No QWK Packet created (no new messages)",sock);
sockprintf(sock,"550 No QWK packet created (no new messages)");
filepos=0;
continue;
}
strcpy(fname,qwkfile);
success=TRUE;
delfile=TRUE;
credits=FALSE;
lprintf("%04d %s downloading QWK packet (%ld bytes)"
,sock,user.alias,flength(fname));
} else if(startup->options&FTP_OPT_INDEX_FILE

rswindell
committed
&& !stricmp(p,startup->index_file_name)
&& !delecmd) {
sprintf(fname,"%sftp%d.tx", scfg.data_dir, sock);
if((fp=fopen(fname,"w+b"))==NULL) {
lprintf("%04d !ERROR %d opening %s",sock,errno,fname);
sockprintf(sock, "451 Insufficient system storage");
filepos=0;
continue;
}
if(!getsize && !getdate)
lprintf("%04d %s downloading index for %s"
,sock,user.alias,vpath(lib,dir,str));
success=TRUE;
credits=FALSE;
tmpfile=TRUE;
fprintf(fp,"%-*s File/Folder Descriptions\r\n"
,INDEX_FNAME_LEN,startup->index_file_name);
if(lib<0) {
/* File Aliases */

rswindell
committed
sprintf(aliasfile,"%sftpalias.cfg",scfg.ctrl_dir);
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
if((alias_fp=fopen(aliasfile,"r"))!=NULL) {
while(!feof(alias_fp)) {
if(!fgets(aliasline,sizeof(aliasline)-1,alias_fp))
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++;
np++; /* description pointer */
while(*np && *np>' ') np++;
while(*np && *np<' ') np++;
truncsp(np);
fprintf(fp,"%-*s %s\r\n",INDEX_FNAME_LEN,p,np);
}
fclose(alias_fp);
}
/* QWK Packet */
if(startup->options&FTP_OPT_ALLOW_QWK /* && fexist(qwkfile) */) {
sprintf(str,"%s.qwk",scfg.sys_id);
fprintf(fp,"%-*s QWK Message Packet\r\n"
,INDEX_FNAME_LEN,str);
}
/* Library Folders */
for(i=0;i<scfg.total_libs;i++) {
if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
continue;
fprintf(fp,"%-*s %s\r\n"
,INDEX_FNAME_LEN,scfg.lib[i]->sname,scfg.lib[i]->lname);
}
} else if(dir<0) {
for(i=0;i<scfg.total_dirs;i++) {
if(scfg.dir[i]->lib!=lib)
continue;
if(i!=scfg.sysop_dir && i!=scfg.upload_dir
&& !chk_ar(&scfg,scfg.dir[i]->ar,&user))
continue;
fprintf(fp,"%-*s %s\r\n"
,INDEX_FNAME_LEN,scfg.dir[i]->code,scfg.dir[i]->lname);
}

rswindell
committed
} else if(chk_ar(&scfg,scfg.dir[dir]->ar,&user)){

rswindell
committed
sprintf(cmd,"%s*",scfg.dir[dir]->path);

rswindell
committed
glob(cmd,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
strcpy(str,g.gl_pathv[i]);
#endif
padfname(getfname(str),f.name);
strupr(f.name);
f.dir=dir;

rswindell
committed
if(getfileixb(&scfg,&f)) {
f.size=flength(g.gl_pathv[i]);

rswindell
committed
fprintf(fp,"%-*s %s\r\n",INDEX_FNAME_LEN
,getfname(g.gl_pathv[i]),f.desc);

rswindell
committed
globfree(&g);
/* HTML Index File */
} else if(startup->options&FTP_OPT_HTML_INDEX_FILE
&& (!stricmp(p,startup->html_index_file)
|| !strnicmp(p,html_index_ext,strlen(html_index_ext)))
#ifdef JAVASCRIPT
js_val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, "name"));
JS_SetProperty(js_cx, js_glob, "ftp_sort", &js_val);
js_val=BOOLEAN_TO_JSVAL(FALSE);
JS_SetProperty(js_cx, js_glob, "ftp_reverse", &js_val);
if(!strnicmp(p,html_index_ext,strlen(html_index_ext))) {
p+=strlen(html_index_ext);
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
tp=strrchr(p,'$');
if(tp!=NULL)
*tp=0;
if(!strnicmp(p,"ext=",4)) {
p+=4;
if(!strcmp(p,"on"))
user.misc|=EXTDESC;
else
user.misc&=~EXTDESC;
if(!(user.rest&FLAG('G')))
putuserrec(&scfg,user.number,U_MISC,8,ultoa(user.misc,str,16));
}
else if(!strnicmp(p,"sort=",5)) {
p+=5;
tp=strchr(p,'&');
if(tp!=NULL) {
*tp=0;
tp++;
if(!stricmp(tp,"reverse")) {
js_val=BOOLEAN_TO_JSVAL(TRUE);
JS_SetProperty(js_cx, js_glob, "ftp_reverse", &js_val);
}
}
js_val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx, p));
JS_SetProperty(js_cx, js_glob, "ftp_sort", &js_val);
}
sprintf(fname,"%sftp%d.tx", scfg.data_dir, sock);
if((fp=fopen(fname,"w+b"))==NULL) {
lprintf("%04d !ERROR %d opening %s",sock,errno,fname);
sockprintf(sock, "451 Insufficient system storage");
filepos=0;
continue;
}
if(!getsize && !getdate)
lprintf("%04d %s downloading HTML index for %s"
,sock,user.alias,vpath(lib,dir,str));
success=TRUE;
credits=FALSE;
tmpfile=TRUE;
delfile=TRUE;
#ifdef JAVASCRIPT
js_val=INT_TO_JSVAL(timeleft);
if(!JS_SetProperty(js_cx, js_user, "time_left", &js_val))
lprintf("%04d !JavaScript ERROR setting user.time_left",sock);
js_generate_index(js_cx, js_glob, sock, fp, lib, dir, &user);
} else if(dir>=0) {
if(!chk_ar(&scfg,scfg.dir[dir]->ar,&user)) {
lprintf("%04d !%s has insufficient access to /%s/%s"
,sock,user.alias,scfg.lib[scfg.dir[dir]->lib]->sname,scfg.dir[dir]->code);
sockprintf(sock,"550 Insufficient access.");
filepos=0;
continue;
}

rswindell
committed
if(!getsize && !getdate && !delecmd
&& !chk_ar(&scfg,scfg.dir[dir]->dl_ar,&user)) {
lprintf("%04d !%s has insufficient access to download from /%s/%s"
,sock,user.alias,scfg.lib[scfg.dir[dir]->lib]->sname,scfg.dir[dir]->code);
sockprintf(sock,"550 Insufficient access.");
filepos=0;
continue;
}

rswindell
committed
if(delecmd && !dir_op(&scfg,&user,dir)) {
lprintf("%04d !%s has insufficient access to delete files in /%s/%s"
,sock,user.alias,scfg.lib[scfg.dir[dir]->lib]->sname,scfg.dir[dir]->code);
sockprintf(sock,"550 Insufficient access.");
filepos=0;
continue;
}
sprintf(fname,"%s%s",scfg.dir[dir]->path,p);
GetShortPathName(fname, str, sizeof(str));
#else
strcpy(str,fname);
#endif
padfname(getfname(str),f.name);
strupr(f.name);
f.dir=dir;
f.cdt=0;
f.size=-1;
filedat=getfileixb(&scfg,&f);
if(!filedat && !(startup->options&FTP_OPT_DIR_FILES)) {
sockprintf(sock,"550 File not found: %s",p);
lprintf("%04d !%s file not in database (%s) for %.4s command"
,sock,user.alias,fname,cmd);
filepos=0;
continue;
}

rswindell
committed
/* Verify credits */
if(!getsize && !getdate && !delecmd
&& !(scfg.dir[dir]->misc&DIR_FREE)
&& !(user.exempt&FLAG('D'))) {
if(filedat)
getfiledat(&scfg,&f);
else
f.cdt=flength(fname);
if(f.cdt>(user.cdt+user.freecdt)) {
lprintf("%04d !%s has insufficient credit to download /%s/%s/%s (%lu credits)"
,sock,user.alias,scfg.lib[scfg.dir[dir]->lib]->sname
,scfg.dir[dir]->code
,p
,f.cdt);
sockprintf(sock,"550 Insufficient credit (%lu required).",f.cdt);
filepos=0;
continue;
}
}
if(strcspn(p,ILLEGAL_FILENAME_CHARS)!=strlen(p)) {
success=FALSE;
lprintf("%04d !%s illegal filename attempt: %s"
,sock,user.alias,p);
hacklog(&scfg, "FTP", user.alias, cmd, host_name, &ftp.client_addr);
} else {
if(fexist(fname)) {
success=TRUE;

rswindell
committed
if(!getsize && !getdate && !delecmd)
lprintf("%04d %s downloading: %s (%ld bytes)"
,sock,user.alias,fname,flength(fname));
socket_debug[sock]|=SOCKET_DEBUG_DOWNLOAD;
if(getsize && success)
sockprintf(sock,"213 %lu",flength(fname));
else if(getdate && success) {
t=fdate(fname);
tm_p=gmtime(&t);
if(tm_p==NULL)
memset(&tm,0,sizeof(tm));
else
tm=*tm_p;
sockprintf(sock,"213 %u%02u%02u%02u%02u%02u"
,1900+tm.tm_year,tm.tm_mon+1,tm.tm_mday
,tm.tm_hour,tm.tm_min,tm.tm_sec);

rswindell
committed
} else if(delecmd && success) {
if(remove(fname)!=0) {
lprintf("%04d !ERROR %d deleting %s",sock,errno,fname);
sockprintf(sock,"450 %s could not be deleted (error: %d)"
,fname,errno);
} else {
lprintf("%04d %s deleted %s",sock,user.alias,fname);
if(filedat)
removefiledat(&scfg,&f);
sockprintf(sock,"250 %s deleted.",fname);
}
} else if(success) {
sockprintf(sock,"150 Opening BINARY mode data connection for file transfer.");
filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,filepos
,&transfer_inprogress,&transfer_aborted,delfile,tmpfile
,&lastactive,&user,dir,FALSE,credits,FALSE,NULL);
}
sockprintf(sock,"550 File not found: %s",p);
lprintf("%04d !%s file not found (%s) for %.4s command"
,sock,user.alias,p,cmd);
socket_debug[sock]&=~SOCKET_DEBUG_DOWNLOAD;
continue;
}
if(!strnicmp(cmd, "DESC", 4)) {
if(user.rest&FLAG('U')) {
sockprintf(sock,"553 Insufficient access.");
continue;
}
p=cmd+4;
while(*p && *p<=' ') p++;
if(*p==0)
sockprintf(sock,"501 No file description given.");
else {
sprintf(desc,"%.*s",(int)sizeof(desc)-1,p);
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
sockprintf(sock,"200 File description set. Ready to STOR file.");
}
continue;
}
if(!strnicmp(cmd, "STOR ", 5)) {
if(user.rest&FLAG('U')) {
sockprintf(sock,"553 Insufficient access.");
continue;
}
if(transfer_inprogress==TRUE) {
lprintf("%04d !TRANSFER already in progress (%s)",sock,cmd);
sockprintf(sock,"425 Transfer already in progress.");
continue;
}
lib=curlib;
dir=curdir;
p=cmd+5;
while(*p && *p<=' ') p++;
if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR)))
p+=strlen(BBS_FSYS_DIR); /* already mounted */
if(*p=='/') {
lib=-1;
p++;
}
else if(!strncmp(p,"./",2))
p+=2;
/* Need to add support for uploading to aliased directories */
if(lib<0 && (tp=strchr(p,'/'))!=NULL) {
dir=-1;
*tp=0;
for(i=0;i<scfg.total_libs;i++) {
if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
continue;
if(!stricmp(scfg.lib[i]->sname,p))
break;
}
if(i<scfg.total_libs)
lib=i;
p=tp+1;
}
if(dir<0 && (tp=strchr(p,'/'))!=NULL) {
*tp=0;
for(i=0;i<scfg.total_dirs;i++) {
if(scfg.dir[i]->lib!=lib)
continue;
if(i!=scfg.sysop_dir && i!=scfg.upload_dir
&& !chk_ar(&scfg,scfg.dir[i]->ar,&user))
continue;
if(!stricmp(scfg.dir[i]->code,p))
break;
}
if(i<scfg.total_dirs)
dir=i;
p=tp+1;
}
if(dir<0) {
sprintf(str,"%s.rep",scfg.sys_id);
if(!(startup->options&FTP_OPT_ALLOW_QWK)
|| stricmp(p,str)) {
lprintf("%04d !%s attempted to upload to invalid directory"
,sock,user.alias);
sockprintf(sock,"553 Invalid directory.");
continue;
}

rswindell
committed
sprintf(fname,"%sfile/%04d.rep",scfg.data_dir,user.number);
lprintf("%04d %s uploading %s"
,sock,user.alias,fname);
} else {
if(!chk_ar(&scfg,scfg.dir[dir]->ul_ar,&user)) {
lprintf("%04d !%s has insufficient access to upload to /%s/%s"
,sock,user.alias,scfg.lib[scfg.dir[dir]->lib]->sname,scfg.dir[dir]->code);
sockprintf(sock,"553 Insufficient access.");
continue;
}
if(strcspn(p,ILLEGAL_FILENAME_CHARS)!=strlen(p)
|| trashcan(&scfg,p,"file")) {
lprintf("%04d !%s illegal filename attempt: %s"
,sock,user.alias,p);
sockprintf(sock,"553 Illegal filename attempt");
hacklog(&scfg, "FTP", user.alias, cmd, host_name, &ftp.client_addr);
continue;
}
sprintf(fname,"%s%s",scfg.dir[dir]->path,p);
if(fexist(fname)
|| (startup->options&FTP_OPT_INDEX_FILE
&& !stricmp(p,startup->index_file_name))
|| (startup->options&FTP_OPT_HTML_INDEX_FILE
&& !stricmp(p,startup->html_index_file))
) {
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
lprintf("%04d !%s attempted to overwrite existing file: %s"
,sock,user.alias,fname);
sockprintf(sock,"553 File already exists.");
continue;
}
lprintf("%04d %s uploading %s to /%s/%s"
,sock,user.alias,fname
,scfg.lib[scfg.dir[dir]->lib]->sname,scfg.dir[dir]->code);
}
sockprintf(sock,"150 Opening BINARY mode data connection for file transfer.");
filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,filepos
,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
,&lastactive
,&user
,dir
,TRUE /* uploading */
,TRUE /* credits */
,FALSE /* append */
,desc
);
filepos=0;
continue;
}
if(!stricmp(cmd,"CDUP") || !stricmp(cmd,"XCUP")) {
if(curdir<0)
curlib=-1;
else
curdir=-1;
sockprintf(sock,"200 CDUP command successful.");
continue;
}
if(!strnicmp(cmd, "CWD ", 4) || !strnicmp(cmd,"XCWD ",5)) {
p=cmd+4;
while(*p && *p<=' ') p++;
if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR)))
p+=strlen(BBS_FSYS_DIR); /* already mounted */
if(*p=='/') {
curlib=-1;
curdir=-1;
p++;
}
/* Local File System? */
if(sysop && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)
&& !strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) {
p+=strlen(LOCAL_FSYS_DIR);
if(!direxist(p)) {
sockprintf(sock,"550 Directory does not exist.");
lprintf("%04d !%s attempted to mount invalid directory: %s"
,sock, user.alias, p);
continue;
}
sprintf(local_dir,"%.*s",(int)sizeof(local_dir)-1,p);
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
local_fsys=TRUE;
sockprintf(sock,"250 CWD command successful (local file system mounted).");
lprintf("%04d %s mounted local file system", sock, user.alias);
continue;
}
success=FALSE;
/* Directory Alias? */
if(curlib<0 && alias(p,NULL,&user,&curdir)==TRUE) {
if(curdir>=0)
curlib=scfg.dir[curdir]->lib;
success=TRUE;
}
orglib=curlib;
orgdir=curdir;
tp=0;
if(!strncmp(p,"...",3)) {
curlib=-1;
curdir=-1;
p+=3;
}
if(!strncmp(p,"./",2))
p+=2;
else if(!strncmp(p,"..",2)) {
if(curdir<0)
curlib=-1;
else
curdir=-1;
p+=2;
}
if(*p==0)
success=TRUE;
else if(!strcmp(p,"."))
success=TRUE;
if(!success && (curlib<0 || *p=='/')) { /* Root dir */
if(*p=='/') p++;
tp=strchr(p,'/');
if(tp) *tp=0;
for(i=0;i<scfg.total_libs;i++) {
if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
continue;
if(!stricmp(scfg.lib[i]->sname,p))
break;
}
if(i<scfg.total_libs) {
curlib=i;
success=TRUE;
}
}
if((!success && curdir<0) || (success && tp && *(tp+1))) {
if(tp)
p=tp+1;
tp=strchr(p,'/');
if(tp) *tp=0;
for(i=0;i<scfg.total_dirs;i++) {
if(scfg.dir[i]->lib!=curlib)
continue;
if(i!=scfg.sysop_dir && i!=scfg.upload_dir
&& !chk_ar(&scfg,scfg.dir[i]->ar,&user))
continue;
if(!stricmp(scfg.dir[i]->code,p))
break;
}
if(i<scfg.total_dirs) {
curdir=i;
success=TRUE;
} else
success=FALSE;
}
if(success)
sockprintf(sock,"250 CWD command successful.");
else {
sockprintf(sock,"550 %s: No such file or directory.",p);
curlib=orglib;
curdir=orgdir;
}
continue;
}
if(!stricmp(cmd, "PWD") || !stricmp(cmd,"XPWD")) {
if(curlib<0)
sockprintf(sock,"257 \"/\" is current directory.");
else if(curdir<0)
sockprintf(sock,"257 \"/%s\" is current directory."
,scfg.lib[curlib]->sname);
else
sockprintf(sock,"257 \"/%s/%s\" is current directory."
,scfg.lib[curlib]->sname,scfg.dir[curdir]->code);
continue;
}
if(!strnicmp(cmd, "MKD", 3) ||
!strnicmp(cmd,"XMKD",4) ||
!strnicmp(cmd,"SITE EXEC",9))
hacklog(&scfg, "FTP", user.alias, cmd, host_name, &ftp.client_addr);
sockprintf(sock,"500 Syntax error: '%s'",cmd);
lprintf("%04d !FTP: UNSUPPORTED COMMAND: '%s'",sock,cmd);
} /* while(1) */
if(transfer_inprogress==TRUE) {
lprintf("%04d Waiting for transfer to complete...",sock);
while(data_sock!=INVALID_SOCKET && transfer_inprogress==TRUE) {
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
if(gettimeleft(&scfg,&user,logintime)<1) {
lprintf("%04d Out of time, disconnecting",sock);
sockprintf(sock,"421 Sorry, you've run out of time.");
close_socket(&data_sock,__LINE__);
transfer_aborted=TRUE;
}
if((time(NULL)-lastactive)>startup->max_inactivity) {
lprintf("%04d Disconnecting due to to inactivity.",sock);
sockprintf(sock,"421 Disconnecting due to inactivity (%u seconds)."
,startup->max_inactivity);
close_socket(&data_sock,__LINE__);
transfer_aborted=TRUE;
}
}
lprintf("%04d Done waiting for transfer to complete",sock);
}
/* Update User Statistics */
if(user.number) {
user.tlast=(ushort)(time(NULL)-logintime);
putuserrec(&scfg,user.number,U_LASTON,8,ultoa(time(NULL),str,16));
putuserrec(&scfg,user.number,U_TLAST,5,ultoa(user.tlast,str,10));
adjustuserrec(&scfg,user.number,U_TIMEON,5,user.tlast);
}
if(user.number)
lprintf("%04d %s logged off.",sock,user.alias);
if(startup->hangup_sound[0] && !(startup->options&FTP_OPT_MUTE))
PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME);
#ifdef JAVASCRIPT
/* Free Context */
JS_DestroyContext(js_cx);
#endif
status(STATUS_WFC);
lprintf("%04d CTRL thread terminated", sock);
active_clients--;
update_clients();
client_off(sock);
socket_debug[sock]&=~SOCKET_DEBUG_CTRL;
/* Free up resources here */
close_socket(&sock,__LINE__);
if(pasv_sock!=INVALID_SOCKET)
close_socket(&pasv_sock,__LINE__);
if(data_sock!=INVALID_SOCKET)
close_socket(&data_sock,__LINE__);
thread_down();
}
static void cleanup(int code)
{
if(server_socket!=INVALID_SOCKET)
close_socket(&server_socket,__LINE__);
server_socket=INVALID_SOCKET;
update_clients();
#ifdef _WINSOCKAPI_
if(WSAInitialized && WSACleanup()!=0)
lprintf("0000 !WSACleanup ERROR %d",ERROR_VALUE);
#ifdef JAVASCRIPT
if(js_runtime!=NULL) {
JS_DestroyRuntime(js_runtime);
js_runtime=NULL;
}
#endif
lprintf("#### FTP Server thread terminated");
status("Down");
if(startup!=NULL && startup->terminated!=NULL)
startup->terminated(code);
thread_down();
}
char* DLLCALL ftp_ver(void)
{
static char ver[256];
char compiler[32];

rswindell
committed
COMPILER_DESC(compiler);
,FTP_VERSION
#ifdef _DEBUG
," Debug"
#else
,""
#endif
,__DATE__, __TIME__, compiler);
return(ver);
}
void DLLCALL ftp_server(void* arg)
{
char compiler[32];
SOCKADDR_IN server_addr;
SOCKADDR_IN client_addr;
int client_addr_len;
SOCKET client_socket;
int i;
int result;
time_t t;
time_t start;
LINGER linger;

rswindell
committed
fd_set socket_set;

rswindell
committed
struct timeval tv;
startup=(ftp_startup_t*)arg;
if(startup==NULL) {
sbbs_beep(100,500);
fprintf(stderr, "No startup structure passed!\n");
return;
}
if(startup->size!=sizeof(ftp_startup_t)) { /* verify size */
sbbs_beep(100,500);
sbbs_beep(300,500);
sbbs_beep(100,500);
fprintf(stderr, "Invalid startup structure!\n");
return;
}
/* Setup intelligent defaults */
if(startup->port==0) startup->port=IPPORT_FTP;
if(startup->max_inactivity==0) startup->max_inactivity=300; /* seconds */
if(startup->index_file_name[0]==0) strcpy(startup->index_file_name,"00index");
if(startup->html_index_file[0]==0) strcpy(startup->html_index_file,"00index.html");
if(startup->html_index_script[0]==0) strcpy(startup->html_index_script,"ftp-html.js");
/*temporary*/
startup->options|=FTP_OPT_HTML_INDEX_FILE;
thread_up();
status("Initializing");
#ifdef __unix__ /* Ignore "Broken Pipe" signal */
signal(SIGPIPE,SIG_IGN);
#endif
lprintf("Synchronet FTP Server Version %s%s"
,FTP_VERSION
#ifdef _DEBUG
," Debug"
#else
,""
#endif
);

rswindell
committed
COMPILER_DESC(compiler);
lprintf("Compiled %s %s with %s", __DATE__, __TIME__, compiler);
srand(time(NULL));

rswindell
committed
if(!(startup->options&FTP_OPT_LOCAL_TIMEZONE)) {
if(PUTENV("TZ=UCT0"))
lprintf("!putenv() FAILED");
tzset();
if((t=checktime())!=0) { /* Check binary time */
lprintf("!TIME PROBLEM (%ld)",t);
cleanup(1);
return;
}
}
if(!winsock_startup()) {
cleanup(1);
return;
}
lprintf("Initializing on %.24s with options: %lx"
#ifdef _WIN32
if((socket_mutex=CreateMutex(NULL,FALSE,NULL))==NULL) {
lprintf("!ERROR %d creating socket_mutex", GetLastError());
cleanup(1);
return;
}
/* Initial configuration and load from CNF files */
memset(&scfg, 0, sizeof(scfg));
sprintf(scfg.ctrl_dir, "%.*s",(int)sizeof(scfg.ctrl_dir)-1
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
,startup->ctrl_dir);
lprintf("Loading configuration files from %s", scfg.ctrl_dir);
if(!load_cfg(&scfg, NULL)) {
lprintf("!Failed to load configuration files");
cleanup(1);
return;
}
if(!startup->max_clients) {
startup->max_clients=scfg.sys_nodes;
if(startup->max_clients<10)
startup->max_clients=10;
}
lprintf("Maximum clients: %d",startup->max_clients);
lprintf("Maximum inactivity: %d seconds",startup->max_inactivity);
active_clients=0;
update_clients();
strlwr(scfg.sys_id); /* Use lower-case unix-looking System ID for group name */
for(i=0;i<scfg.total_libs;i++) {
strlwr(scfg.lib[i]->sname);
dotname(scfg.lib[i]->sname,scfg.lib[i]->sname);
}
for(i=0;i<scfg.total_dirs;i++)
strlwr(scfg.dir[i]->code);
#ifdef JAVASCRIPT
if((js_runtime = JS_NewRuntime(JAVASCRIPT_RUNTIME_MEMORY))==NULL) {
lprintf("!JS_NewRuntime failed");
cleanup(1);
return;
}
#endif
/* open a socket and wait for a client */
if((server_socket=open_socket(SOCK_STREAM))==INVALID_SOCKET) {
lprintf("!ERROR %d opening socket", ERROR_VALUE);
cleanup(1);
return;
}
lprintf("%04d FTP socket opened",server_socket);
#if 1
linger.l_onoff=TRUE;
linger.l_linger=5; /* seconds */
if((result=setsockopt(server_socket, SOL_SOCKET, SO_LINGER
,(char *)&linger, sizeof(linger)))!=0) {
lprintf ("%04d !ERROR %d (%d) setting socket options."
,server_socket, result, ERROR_VALUE);
cleanup(1);
return;
}
#endif
/*****************************/
/* Listen for incoming calls */
/*****************************/
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(startup->port);
if((result=bind(server_socket, (struct sockaddr *) &server_addr
,sizeof(server_addr)))!=0) {
lprintf("%04d !ERROR %d (%d) binding socket to port %d"
,server_socket, result, ERROR_VALUE,startup->port);
cleanup(1);
return;
}
if((result=listen(server_socket, 1))!= 0) {
lprintf("%04d !ERROR %d (%d) listening on socket"
,server_socket, result, ERROR_VALUE);
cleanup(1);
return;
}
/* signal caller that we've started up successfully */
if(startup->started!=NULL)
startup->started();
lprintf("%04d FTP Server thread started on port %d",server_socket,startup->port);
status(STATUS_WFC);
while(server_socket!=INVALID_SOCKET) {
/* now wait for connection */

rswindell
committed
tv.tv_sec=2;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(server_socket,&socket_set);
if((i=select(server_socket+1,&socket_set,NULL,NULL,&tv))<1) {

rswindell
committed
if(i==0) {
mswait(1);
continue;
}
if(ERROR_VALUE==EINTR)
lprintf("0000 FTP Server listening interrupted");
else if(ERROR_VALUE == ENOTSOCK)
lprintf("0000 FTP Server sockets closed");
else
lprintf("0000 !ERROR %d selecting sockets",ERROR_VALUE);
break;
}
client_addr_len = sizeof(client_addr);
client_socket = accept(server_socket, (struct sockaddr *)&client_addr
,&client_addr_len);
if(client_socket == INVALID_SOCKET)
{
lprintf("0000 FTP socket closed while listening");
lprintf("0000 !accept failed (ERROR %d)", ERROR_VALUE);
break;
}
if(startup->socket_open!=NULL)
startup->socket_open(TRUE);
sockets++;
if(active_clients>=startup->max_clients) {
lprintf("%04d !MAXMIMUM CLIENTS (%d) reached, access denied"
,client_socket, startup->max_clients);
sockprintf(client_socket,"421 Maximum active clients reached, please try again later.");
close_socket(&client_socket,__LINE__);
continue;
}
if((ftp=malloc(sizeof(ftp_t)))==NULL) {
lprintf("%04d !ERROR allocating %d bytes of memory for ftp_t"
,client_socket,sizeof(ftp_t));
sockprintf(client_socket,"421 System error, please try again later.");
close_socket(&client_socket,__LINE__);
continue;
}
ftp->socket=client_socket;
ftp->client_addr=client_addr;
_beginthread (ctrl_thread, 0, ftp);
}
if(active_clients) {
lprintf("0000 Waiting for %d active clients to disconnect...", active_clients);
start=time(NULL);
while(active_clients) {
if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
lprintf("0000 !TIMEOUT waiting for %d active clients ",active_clients);