|
|
1.1 root 1: /* ftpsrvr.c */
2:
3: /* Synchronet FTP server */
4:
5: /* $Id: ftpsrvr.c,v 1.313 2006/12/29 08:42:56 rswindell Exp $ */
6:
7: /****************************************************************************
8: * @format.tab-size 4 (Plain Text/Source Code File Header) *
9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
10: * *
11: * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html *
12: * *
13: * This program is free software; you can redistribute it and/or *
14: * modify it under the terms of the GNU General Public License *
15: * as published by the Free Software Foundation; either version 2 *
16: * of the License, or (at your option) any later version. *
17: * See the GNU General Public License for more details: gpl.txt or *
18: * http://www.fsf.org/copyleft/gpl.html *
19: * *
20: * Anonymous FTP access to the most recent released source is available at *
21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
22: * *
23: * Anonymous CVS access to the development source and modification history *
24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
26: * (just hit return, no password is necessary) *
27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
28: * *
29: * For Synchronet coding style and modification guidelines, see *
30: * http://www.synchro.net/source.html *
31: * *
32: * You are encouraged to submit any modifications (preferably in Unix diff *
33: * format) via e-mail to [email protected] *
34: * *
35: * Note: If this box doesn't appear square, then you need to fix your tabs. *
36: ****************************************************************************/
37:
38: /* ANSI C Library headers */
39: #include <stdio.h>
40: #include <stdlib.h> /* ltoa in GNU C lib */
41: #include <stdarg.h> /* va_list, varargs */
42: #include <string.h> /* strrchr */
43: #include <fcntl.h> /* O_WRONLY, O_RDONLY, etc. */
44: #include <errno.h> /* EACCES */
45: #include <ctype.h> /* toupper */
46: #include <sys/stat.h> /* S_IWRITE */
47:
48: /* Synchronet-specific headers */
49: #undef SBBS /* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
50: #include "sbbs.h"
51: #include "text.h" /* TOTAL_TEXT */
52: #include "ftpsrvr.h"
53: #include "telnet.h"
54:
55: /* Constants */
56:
57: #define FTP_SERVER "Synchronet FTP Server"
58:
59: #define STATUS_WFC "Listening"
60: #define ANONYMOUS "anonymous"
61:
62: #define BBS_VIRTUAL_PATH "bbs:/""/" /* this is actually bbs:<slash><slash> */
63: #define LOCAL_FSYS_DIR "local:"
64: #define BBS_FSYS_DIR "bbs:"
65: #define BBS_HIDDEN_ALIAS "hidden"
66:
67: #define TIMEOUT_THREAD_WAIT 60 /* Seconds */
68:
69: #define TIMEOUT_SOCKET_LISTEN 30 /* Seconds */
70:
71: #define XFER_REPORT_INTERVAL 60 /* Seconds */
72:
73: #define INDEX_FNAME_LEN 15
74:
75: #define NAME_LEN 15 /* User name length for listings */
76:
77: static ftp_startup_t* startup=NULL;
78: static scfg_t scfg;
79: static SOCKET server_socket=INVALID_SOCKET;
80: static DWORD active_clients=0;
81: static DWORD sockets=0;
82: static DWORD thread_count=0;
83: static time_t uptime=0;
84: static DWORD served=0;
85: static BOOL terminate_server=FALSE;
86: static char revision[16];
87: static char *text[TOTAL_TEXT];
88: static str_list_t recycle_semfiles;
89: static str_list_t shutdown_semfiles;
90:
91: #ifdef _DEBUG
92: static BYTE socket_debug[0x10000]={0};
93:
94: #define SOCKET_DEBUG_CTRL (1<<0) /* 0x01 */
95: #define SOCKET_DEBUG_SEND (1<<1) /* 0x02 */
96: #define SOCKET_DEBUG_READLINE (1<<2) /* 0x04 */
97: #define SOCKET_DEBUG_ACCEPT (1<<3) /* 0x08 */
98: #define SOCKET_DEBUG_SENDTHREAD (1<<4) /* 0x10 */
99: #define SOCKET_DEBUG_TERMINATE (1<<5) /* 0x20 */
100: #define SOCKET_DEBUG_RECV_CHAR (1<<6) /* 0x40 */
101: #define SOCKET_DEBUG_FILEXFER (1<<7) /* 0x80 */
102: #endif
103:
104:
105: typedef struct {
106: SOCKET socket;
107: SOCKADDR_IN client_addr;
108: } ftp_t;
109:
110:
111: static const char *ftp_mon[]={"Jan","Feb","Mar","Apr","May","Jun"
112: ,"Jul","Aug","Sep","Oct","Nov","Dec"};
113:
114: BOOL direxist(char *dir)
115: {
116: if(access(dir,0)==0)
117: return(TRUE);
118: else
119: return(FALSE);
120: }
121:
122: BOOL dir_op(scfg_t* cfg, user_t* user, uint dirnum)
123: {
124: return(user->level>=SYSOP_LEVEL || user->exempt&FLAG('R')
125: || (cfg->dir[dirnum]->op_ar[0] && chk_ar(cfg,cfg->dir[dirnum]->op_ar,user)));
126: }
127:
128: static int lprintf(int level, char *fmt, ...)
129: {
130: int result;
131: va_list argptr;
132: char sbuf[1024];
133:
134: if(startup==NULL || startup->lputs==NULL)
135: return(0);
136:
137: va_start(argptr,fmt);
138: vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
139: sbuf[sizeof(sbuf)-1]=0;
140: va_end(argptr);
141: result=startup->lputs(startup->cbdata,level,sbuf);
142:
143: return(result);
144: }
145:
146: #ifdef _WINSOCKAPI_
147:
148: static WSADATA WSAData;
149: #define SOCKLIB_DESC WSAData.szDescription
150:
151: static BOOL WSAInitialized=FALSE;
152:
153: static BOOL winsock_startup(void)
154: {
155: int status; /* Status Code */
156:
157: if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
158: lprintf(LOG_INFO,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
159: WSAInitialized=TRUE;
160: return (TRUE);
161: }
162:
163: lprintf(LOG_ERR,"!WinSock startup ERROR %d", status);
164: return (FALSE);
165: }
166:
167: #else /* No WINSOCK */
168:
169: #define winsock_startup() (TRUE)
170: #define SOCKLIB_DESC NULL
171:
172: #endif
173:
174: static void status(char* str)
175: {
176: if(startup!=NULL && startup->status!=NULL)
177: startup->status(startup->cbdata,str);
178: }
179:
180: static void update_clients(void)
181: {
182: if(startup!=NULL && startup->clients!=NULL)
183: startup->clients(startup->cbdata,active_clients);
184: }
185:
186: static void client_on(SOCKET sock, client_t* client, BOOL update)
187: {
188: if(startup!=NULL && startup->client_on!=NULL)
189: startup->client_on(startup->cbdata,TRUE,sock,client,update);
190: }
191:
192: static void client_off(SOCKET sock)
193: {
194: if(startup!=NULL && startup->client_on!=NULL)
195: startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
196: }
197:
198: static void thread_up(BOOL setuid)
199: {
200: thread_count++;
201: if(startup!=NULL && startup->thread_up!=NULL)
202: startup->thread_up(startup->cbdata,TRUE, setuid);
203: }
204:
205: static void thread_down(void)
206: {
207: if(thread_count>0)
208: thread_count--;
209: if(startup!=NULL && startup->thread_up!=NULL)
210: startup->thread_up(startup->cbdata,FALSE, FALSE);
211: }
212:
213: static SOCKET ftp_open_socket(int type)
214: {
215: SOCKET sock;
216: char error[256];
217:
218: sock=socket(AF_INET, type, IPPROTO_IP);
219: if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
220: startup->socket_open(startup->cbdata,TRUE);
221: if(sock!=INVALID_SOCKET) {
222: if(set_socket_options(&scfg, sock, "FTP", error, sizeof(error)))
223: lprintf(LOG_ERR,"%04d !ERROR %s",sock, error);
224: sockets++;
225: #ifdef _DEBUG
226: lprintf(LOG_DEBUG,"%04d Socket opened (%u sockets in use)",sock,sockets);
227: #endif
228: }
229: return(sock);
230: }
231:
232: #ifdef __BORLANDC__
233: #pragma argsused
234: #endif
235: static int ftp_close_socket(SOCKET* sock, int line)
236: {
237: int result;
238:
239: if((*sock)==INVALID_SOCKET) {
240: lprintf(LOG_WARNING,"0000 !INVALID_SOCKET in close_socket from line %u",line);
241: return(-1);
242: }
243:
244: shutdown(*sock,SHUT_RDWR); /* required on Unix */
245:
246: result=closesocket(*sock);
247: if(startup!=NULL && startup->socket_open!=NULL)
248: startup->socket_open(startup->cbdata,FALSE);
249:
250: sockets--;
251:
252: if(result!=0) {
253: if(ERROR_VALUE!=ENOTSOCK)
254: lprintf(LOG_WARNING,"%04d !ERROR %d closing socket from line %u",*sock,ERROR_VALUE,line);
255: } else if(sock==&server_socket || *sock==server_socket)
256: lprintf(LOG_DEBUG,"%04d Server socket closed (%u sockets in use) from line %u",*sock,sockets,line);
257: #ifdef _DEBUG
258: else
259: lprintf(LOG_DEBUG,"%04d Socket closed (%u sockets in use) from line %u",*sock,sockets,line);
260: #endif
261: *sock=INVALID_SOCKET;
262:
263: return(result);
264: }
265:
266: static int sockprintf(SOCKET sock, char *fmt, ...)
267: {
268: int len;
269: int maxlen;
270: int result;
271: va_list argptr;
272: char sbuf[1024];
273: fd_set socket_set;
274: struct timeval tv;
275:
276: va_start(argptr,fmt);
277: len=vsnprintf(sbuf,maxlen=sizeof(sbuf)-2,fmt,argptr);
278: va_end(argptr);
279:
280: if(len<0 || len>maxlen) /* format error or output truncated */
281: len=maxlen;
282: if(startup!=NULL && startup->options&FTP_OPT_DEBUG_TX)
283: lprintf(LOG_DEBUG,"%04d TX: %.*s", sock, len, sbuf);
284: memcpy(sbuf+len,"\r\n",2);
285: len+=2;
286:
287: if(sock==INVALID_SOCKET) {
288: lprintf(LOG_WARNING,"!INVALID SOCKET in call to sockprintf");
289: return(0);
290: }
291:
292: /* Check socket for writability (using select) */
293: tv.tv_sec=300;
294: tv.tv_usec=0;
295:
296: FD_ZERO(&socket_set);
297: FD_SET(sock,&socket_set);
298:
299: if((result=select(sock+1,NULL,&socket_set,NULL,&tv))<1) {
300: if(result==0)
301: lprintf(LOG_WARNING,"%04d !TIMEOUT selecting socket for send"
302: ,sock);
303: else
304: lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket for send"
305: ,sock, ERROR_VALUE);
306: return(0);
307: }
308: while((result=sendsocket(sock,sbuf,len))!=len) {
309: if(result==SOCKET_ERROR) {
310: if(ERROR_VALUE==EWOULDBLOCK) {
311: YIELD();
312: continue;
313: }
314: if(ERROR_VALUE==ECONNRESET)
315: lprintf(LOG_WARNING,"%04d Connection reset by peer on send",sock);
316: else if(ERROR_VALUE==ECONNABORTED)
317: lprintf(LOG_WARNING,"%04d Connection aborted by peer on send",sock);
318: else
319: lprintf(LOG_WARNING,"%04d !ERROR %d sending",sock,ERROR_VALUE);
320: return(0);
321: }
322: lprintf(LOG_WARNING,"%04d !ERROR: short send: %u instead of %u",sock,result,len);
323: }
324: return(len);
325: }
326:
327:
328: /* Returns the directory index of a virtual lib/dir path (e.g. main/games/filename) */
329: int getdir(char* p, user_t* user)
330: {
331: char* tp;
332: char path[MAX_PATH+1];
333: int dir;
334: int lib;
335:
336: SAFECOPY(path,p);
337: p=path;
338:
339: if(*p=='/')
340: p++;
341: else if(!strncmp(p,"./",2))
342: p+=2;
343:
344: tp=strchr(p,'/');
345: if(tp) *tp=0;
346: for(lib=0;lib<scfg.total_libs;lib++) {
347: if(!chk_ar(&scfg,scfg.lib[lib]->ar,user))
348: continue;
349: if(!stricmp(scfg.lib[lib]->sname,p))
350: break;
351: }
352: if(lib>=scfg.total_libs)
353: return(-1);
354:
355: if(tp!=NULL)
356: p=tp+1;
357:
358: tp=strchr(p,'/');
359: if(tp) *tp=0;
360: for(dir=0;dir<scfg.total_dirs;dir++) {
361: if(scfg.dir[dir]->lib!=lib)
362: continue;
363: if(dir!=scfg.sysop_dir && dir!=scfg.upload_dir
364: && !chk_ar(&scfg,scfg.dir[dir]->ar,user))
365: continue;
366: if(!stricmp(scfg.dir[dir]->code_suffix,p))
367: break;
368: }
369: if(dir>=scfg.total_dirs)
370: return(-1);
371:
372: return(dir);
373: }
374:
375: /*********************************/
376: /* JavaScript Data and Functions */
377: /*********************************/
378: #ifdef JAVASCRIPT
379:
380: js_server_props_t js_server_props;
381:
382: static JSBool
383: js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
384: {
385: uintN i;
386: JSString* str=NULL;
387: FILE* fp;
388:
389: if((fp=(FILE*)JS_GetContextPrivate(cx))==NULL)
390: return(JS_FALSE);
391:
392: for (i = 0; i < argc; i++) {
393: str = JS_ValueToString(cx, argv[i]);
394: if (!str)
395: return JS_FALSE;
396: fprintf(fp,"%s",JS_GetStringBytes(str));
397: }
398:
399: if(str==NULL)
400: *rval = JSVAL_VOID;
401: else
402: *rval = STRING_TO_JSVAL(str);
403: return(JS_TRUE);
404: }
405:
406: static JSBool
407: js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
408: {
409: FILE* fp;
410:
411: if((fp=(FILE*)JS_GetContextPrivate(cx))==NULL)
412: return(JS_FALSE);
413:
414: js_write(cx,obj,argc,argv,rval);
415: fprintf(fp,"\r\n");
416: return(JS_TRUE);
417: }
418:
419: static JSFunctionSpec js_global_functions[] = {
420: {"write", js_write, 1}, /* write to HTML file */
421: {"writeln", js_writeln, 1}, /* write to HTML file */
422: {"print", js_writeln, 1}, /* alias for writeln */
423: {0}
424: };
425:
426: static void
427: js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
428: {
429: char line[64];
430: char file[MAX_PATH+1];
431: char* warning;
432: FILE* fp;
433:
434: fp=(FILE*)JS_GetContextPrivate(cx);
435:
436: if(report==NULL) {
437: lprintf(LOG_ERR,"!JavaScript: %s", message);
438: if(fp!=NULL)
439: fprintf(fp,"!JavaScript: %s", message);
440: return;
441: }
442:
443: if(report->filename)
444: sprintf(file," %s",report->filename);
445: else
446: file[0]=0;
447:
448: if(report->lineno)
449: sprintf(line," line %u",report->lineno);
450: else
451: line[0]=0;
452:
453: if(JSREPORT_IS_WARNING(report->flags)) {
454: if(JSREPORT_IS_STRICT(report->flags))
455: warning="strict warning";
456: else
457: warning="warning";
458: } else
459: warning="";
460:
461: lprintf(LOG_ERR,"!JavaScript %s%s%s: %s",warning,file,line,message);
462: if(fp!=NULL)
463: fprintf(fp,"!JavaScript %s%s%s: %s",warning,file,line,message);
464: }
465:
466: static JSContext*
467: js_initcx(JSRuntime* runtime, SOCKET sock, JSObject** glob, JSObject** ftp)
468: {
469: JSContext* js_cx;
470: JSObject* js_glob;
471: BOOL success=FALSE;
472:
473: lprintf(LOG_DEBUG,"%04d JavaScript: Initializing context (stack: %lu bytes)"
474: ,sock,startup->js.cx_stack);
475:
476: if((js_cx = JS_NewContext(runtime, startup->js.cx_stack))==NULL)
477: return(NULL);
478:
479: lprintf(LOG_DEBUG,"%04d JavaScript: Context created",sock);
480:
481: JS_SetErrorReporter(js_cx, js_ErrorReporter);
482:
483: do {
484:
485: lprintf(LOG_DEBUG,"%04d JavaScript: Initializing Global object",sock);
486: if((js_glob=js_CreateGlobalObject(js_cx, &scfg, NULL))==NULL)
487: break;
488:
489: if (!JS_DefineFunctions(js_cx, js_glob, js_global_functions))
490: break;
491:
492: lprintf(LOG_DEBUG,"%04d JavaScript: Initializing System object",sock);
493: if(js_CreateSystemObject(js_cx, js_glob, &scfg, uptime, startup->host_name, SOCKLIB_DESC)==NULL)
494: break;
495:
496: if((*ftp=JS_DefineObject(js_cx, js_glob, "ftp", NULL
497: ,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))==NULL)
498: break;
499:
500: if(js_CreateServerObject(js_cx,js_glob,&js_server_props)==NULL)
501: break;
502:
503: if(glob!=NULL)
504: *glob=js_glob;
505:
506: success=TRUE;
507:
508: } while(0);
509:
510: if(!success) {
511: JS_DestroyContext(js_cx);
512: return(NULL);
513: }
514:
515: return(js_cx);
516: }
517:
518: BOOL js_add_file(JSContext* js_cx, JSObject* array,
519: char* name, char* desc, char* ext_desc,
520: ulong size, ulong credits,
521: time_t time, time_t uploaded, time_t last_downloaded,
522: ulong times_downloaded, ulong misc,
523: char* uploader, char* link)
524: {
525: JSObject* file;
526: JSString* js_str;
527: jsval val;
528: jsuint index;
529:
530: if((file=JS_NewObject(js_cx, NULL, NULL, NULL))==NULL)
531: return(FALSE);
532:
533: if((js_str=JS_NewStringCopyZ(js_cx, name))==NULL)
534: return(FALSE);
535: val=STRING_TO_JSVAL(js_str);
536: if(!JS_SetProperty(js_cx, file, "name", &val))
537: return(FALSE);
538:
539: if((js_str=JS_NewStringCopyZ(js_cx, desc))==NULL)
540: return(FALSE);
541: val=STRING_TO_JSVAL(js_str);
542: if(!JS_SetProperty(js_cx, file, "description", &val))
543: return(FALSE);
544:
545: if((js_str=JS_NewStringCopyZ(js_cx, ext_desc))==NULL)
546: return(FALSE);
547: val=STRING_TO_JSVAL(js_str);
548: if(!JS_SetProperty(js_cx, file, "extended_description", &val))
549: return(FALSE);
550:
551: val=INT_TO_JSVAL(size);
552: if(!JS_SetProperty(js_cx, file, "size", &val))
553: return(FALSE);
554:
555: val=INT_TO_JSVAL(credits);
556: if(!JS_SetProperty(js_cx, file, "credits", &val))
557: return(FALSE);
558:
559: JS_NewNumberValue(js_cx,time,&val);
560: if(!JS_SetProperty(js_cx, file, "time", &val))
561: return(FALSE);
562:
563: val=INT_TO_JSVAL(uploaded);
564: if(!JS_SetProperty(js_cx, file, "uploaded", &val))
565: return(FALSE);
566:
567: val=INT_TO_JSVAL(last_downloaded);
568: if(!JS_SetProperty(js_cx, file, "last_downloaded", &val))
569: return(FALSE);
570:
571: val=INT_TO_JSVAL(times_downloaded);
572: if(!JS_SetProperty(js_cx, file, "times_downloaded", &val))
573: return(FALSE);
574:
575: if((js_str=JS_NewStringCopyZ(js_cx, uploader))==NULL)
576: return(FALSE);
577: val=STRING_TO_JSVAL(js_str);
578: if(!JS_SetProperty(js_cx, file, "uploader", &val))
579: return(FALSE);
580:
581: val=INT_TO_JSVAL(misc);
582: if(!JS_SetProperty(js_cx, file, "settings", &val))
583: return(FALSE);
584:
585: if((js_str=JS_NewStringCopyZ(js_cx, link))==NULL)
586: return(FALSE);
587: val=STRING_TO_JSVAL(js_str);
588: if(!JS_SetProperty(js_cx, file, "link", &val))
589: return(FALSE);
590:
591: if(!JS_GetArrayLength(js_cx, array, &index))
592: return(FALSE);
593:
594: val=OBJECT_TO_JSVAL(file);
595: return(JS_SetElement(js_cx, array, index, &val));
596: }
597:
598: BOOL js_generate_index(JSContext* js_cx, JSObject* parent,
599: SOCKET sock, FILE* fp, int lib, int dir, user_t* user)
600: {
601: char str[256];
602: char path[MAX_PATH+1];
603: char spath[MAX_PATH+1]; /* script path */
604: char vpath[MAX_PATH+1]; /* virtual path */
605: char aliasfile[MAX_PATH+1];
606: char extdesc[513];
607: char* p;
608: char* tp;
609: char* np;
610: char* dp;
611: char aliasline[512];
612: BOOL alias_dir;
613: BOOL success=FALSE;
614: FILE* alias_fp;
615: uint i;
616: file_t f;
617: glob_t g;
618: jsval val;
619: jsval rval;
620: JSObject* lib_obj=NULL;
621: JSObject* dir_obj=NULL;
622: JSObject* file_array=NULL;
623: JSObject* dir_array=NULL;
624: JSScript* js_script=NULL;
625: JSString* js_str;
626:
627: JS_SetContextPrivate(js_cx, fp);
628:
629: do { /* pseudo try/catch */
630:
631: if((file_array=JS_NewArrayObject(js_cx, 0, NULL))==NULL) {
632: lprintf(LOG_ERR,"%04d !JavaScript FAILED to create file_array",sock);
633: break;
634: }
635:
636: /* file[] */
637: val=OBJECT_TO_JSVAL(file_array);
638: if(!JS_SetProperty(js_cx, parent, "file_list", &val)) {
639: lprintf(LOG_ERR,"%04d !JavaScript FAILED to set file property",sock);
640: break;
641: }
642:
643: if((dir_array=JS_NewArrayObject(js_cx, 0, NULL))==NULL) {
644: lprintf(LOG_ERR,"%04d !JavaScript FAILED to create dir_array",sock);
645: break;
646: }
647:
648: /* dir[] */
649: val=OBJECT_TO_JSVAL(dir_array);
650: if(!JS_SetProperty(js_cx, parent, "dir_list", &val)) {
651: lprintf(LOG_ERR,"%04d !JavaScript FAILED to set dir property",sock);
652: break;
653: }
654:
655: if(strcspn(startup->html_index_script,"/\\")==strlen(startup->html_index_script)) {
656: sprintf(spath,"%s%s",scfg.mods_dir,startup->html_index_script);
657: if(scfg.mods_dir[0]==0 || !fexist(spath))
658: sprintf(spath,"%s%s",scfg.exec_dir,startup->html_index_script);
659: } else
660: sprintf(spath,"%.*s",(int)sizeof(spath)-4,startup->html_index_script);
661: /* Add extension if not specified */
662: if(!strchr(spath,'.'))
663: strcat(spath,".js");
664:
665: if(!fexist(spath)) {
666: lprintf(LOG_ERR,"%04d !HTML JavaScript (%s) doesn't exist",sock,spath);
667: break;
668: }
669:
670: if((js_str=JS_NewStringCopyZ(js_cx, startup->html_index_file))==NULL)
671: break;
672: val=STRING_TO_JSVAL(js_str);
673: if(!JS_SetProperty(js_cx, parent, "html_index_file", &val)) {
674: lprintf(LOG_ERR,"%04d !JavaScript FAILED to set html_index_file property",sock);
675: break;
676: }
677:
678: /* curlib */
679: if((lib_obj=JS_NewObject(js_cx, NULL, 0, NULL))==NULL) {
680: lprintf(LOG_ERR,"%04d !JavaScript FAILED to create lib_obj",sock);
681: break;
682: }
683:
684: val=OBJECT_TO_JSVAL(lib_obj);
685: if(!JS_SetProperty(js_cx, parent, "curlib", &val)) {
686: lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curlib property",sock);
687: break;
688: }
689:
690: /* curdir */
691: if((dir_obj=JS_NewObject(js_cx, NULL, 0, NULL))==NULL) {
692: lprintf(LOG_ERR,"%04d !JavaScript FAILED to create dir_obj",sock);
693: break;
694: }
695:
696: val=OBJECT_TO_JSVAL(dir_obj);
697: if(!JS_SetProperty(js_cx, parent, "curdir", &val)) {
698: lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir property",sock);
699: break;
700: }
701:
702: SAFECOPY(vpath,"/");
703:
704: if(lib>=0) { /* root */
705:
706: strcat(vpath,scfg.lib[lib]->sname);
707: strcat(vpath,"/");
708:
709: if((js_str=JS_NewStringCopyZ(js_cx, scfg.lib[lib]->sname))==NULL)
710: break;
711: val=STRING_TO_JSVAL(js_str);
712: if(!JS_SetProperty(js_cx, lib_obj, "name", &val)) {
713: lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curlib.name property",sock);
714: break;
715: }
716:
717: if((js_str=JS_NewStringCopyZ(js_cx, scfg.lib[lib]->lname))==NULL)
718: break;
719: val=STRING_TO_JSVAL(js_str);
720: if(!JS_SetProperty(js_cx, lib_obj, "description", &val)) {
721: lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curlib.desc property",sock);
722: break;
723: }
724: }
725:
726: if(dir>=0) { /* 1st level */
727: strcat(vpath,scfg.dir[dir]->code_suffix);
728: strcat(vpath,"/");
729:
730: if((js_str=JS_NewStringCopyZ(js_cx, scfg.dir[dir]->code))==NULL)
731: break;
732: val=STRING_TO_JSVAL(js_str);
733: if(!JS_SetProperty(js_cx, dir_obj, "code", &val)) {
734: lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir.code property",sock);
735: break;
736: }
737:
738: if((js_str=JS_NewStringCopyZ(js_cx, scfg.dir[dir]->sname))==NULL)
739: break;
740: val=STRING_TO_JSVAL(js_str);
741: if(!JS_SetProperty(js_cx, dir_obj, "name", &val)) {
742: lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir.name property",sock);
743: break;
744: }
745:
746: if((js_str=JS_NewStringCopyZ(js_cx, scfg.dir[dir]->lname))==NULL)
747: break;
748: val=STRING_TO_JSVAL(js_str);
749: if(!JS_SetProperty(js_cx, dir_obj, "description", &val)) {
750: lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir.desc property",sock);
751: break;
752: }
753:
754: val=INT_TO_JSVAL(scfg.dir[dir]->misc);
755: if(!JS_SetProperty(js_cx, dir_obj, "settings", &val)) {
756: lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir.misc property",sock);
757: break;
758: }
759: }
760:
761: if((js_str=JS_NewStringCopyZ(js_cx, vpath))==NULL)
762: break;
763: val=STRING_TO_JSVAL(js_str);
764: if(!JS_SetProperty(js_cx, parent, "path", &val)) {
765: lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir property",sock);
766: break;
767: }
768:
769: if(lib<0) { /* root dir */
770:
771: /* File Aliases */
772: sprintf(path,"%sftpalias.cfg",scfg.ctrl_dir);
773: if((alias_fp=fopen(path,"r"))!=NULL) {
774:
775: while(!feof(alias_fp)) {
776: if(!fgets(aliasline,sizeof(aliasline),alias_fp))
777: break;
778:
779: p=aliasline; /* alias pointer */
780: while(*p && *p<=' ') p++;
781:
782: if(*p==';') /* comment */
783: continue;
784:
785: tp=p; /* terminator pointer */
786: while(*tp && *tp>' ') tp++;
787: if(*tp) *tp=0;
788:
789: np=tp+1; /* filename pointer */
790: while(*np && *np<=' ') np++;
791:
792: tp=np; /* terminator pointer */
793: while(*tp && *tp>' ') tp++;
794: if(*tp) *tp=0;
795:
796: dp=tp+1; /* description pointer */
797: while(*dp && *dp<=' ') dp++;
798: truncsp(dp);
799:
800: if(stricmp(dp,BBS_HIDDEN_ALIAS)==0)
801: continue;
802:
803: alias_dir=FALSE;
804:
805: /* Virtual Path? */
806: if(!strnicmp(np,BBS_VIRTUAL_PATH,strlen(BBS_VIRTUAL_PATH))) {
807: if((dir=getdir(np+strlen(BBS_VIRTUAL_PATH),user))<0)
808: continue; /* No access or invalid virtual path */
809: tp=strrchr(np,'/');
810: if(tp==NULL)
811: continue;
812: tp++;
813: if(*tp) {
814: SAFEPRINTF2(aliasfile,"%s%s",scfg.dir[dir]->path,tp);
815: np=aliasfile;
816: }
817: else
818: alias_dir=TRUE;
819: }
820:
821: if(!alias_dir && !fexist(np))
822: continue;
823:
824: if(alias_dir) {
825: if(!chk_ar(&scfg,scfg.dir[dir]->ar,user))
826: continue;
827: SAFEPRINTF2(vpath,"/%s/%s",p,startup->html_index_file);
828: } else
829: SAFECOPY(vpath,p);
830: js_add_file(js_cx
831: ,alias_dir ? dir_array : file_array
832: ,p /* filename */
833: ,dp /* description */
834: ,NULL /* extdesc */
835: ,flength(np) /* size */
836: ,0 /* credits */
837: ,fdate(np) /* time */
838: ,fdate(np) /* uploaded */
839: ,0 /* last downloaded */
840: ,0 /* times downloaded */
841: ,0 /* misc */
842: ,scfg.sys_id /* uploader */
843: ,vpath /* link */
844: );
845: }
846:
847: fclose(alias_fp);
848: }
849:
850: /* QWK Packet */
851: if(startup->options&FTP_OPT_ALLOW_QWK /* && fexist(qwkfile) */) {
852: sprintf(str,"%s.qwk",scfg.sys_id);
853: SAFEPRINTF(vpath,"/%s",str);
854: js_add_file(js_cx
855: ,file_array
856: ,str /* filename */
857: ,"QWK Message Packet" /* description */
858: ,NULL /* extdesc */
859: ,10240 /* size */
860: ,0 /* credits */
861: ,time(NULL) /* time */
862: ,0 /* uploaded */
863: ,0 /* last downloaded */
864: ,0 /* times downloaded */
865: ,0 /* misc */
866: ,scfg.sys_id /* uploader */
867: ,str /* link */
868: );
869: }
870:
871: /* Library Folders */
872: for(i=0;i<scfg.total_libs;i++) {
873: if(!chk_ar(&scfg,scfg.lib[i]->ar,user))
874: continue;
875: SAFEPRINTF2(vpath,"/%s/%s",scfg.lib[i]->sname,startup->html_index_file);
876: js_add_file(js_cx
877: ,dir_array
878: ,scfg.lib[i]->sname /* filename */
879: ,scfg.lib[i]->lname /* description */
880: ,NULL /* extdesc */
881: ,0,0,0,0,0,0,0 /* unused */
882: ,scfg.sys_id /* uploader */
883: ,vpath /* link */
884: );
885: }
886: } else if(dir<0) {
887: /* Directories */
888: for(i=0;i<scfg.total_dirs;i++) {
889: if(scfg.dir[i]->lib!=lib)
890: continue;
891: if(/* i!=scfg.sysop_dir && i!=scfg.upload_dir && */
892: !chk_ar(&scfg,scfg.dir[i]->ar,user))
893: continue;
894: SAFEPRINTF3(vpath,"/%s/%s/%s"
895: ,scfg.lib[scfg.dir[i]->lib]->sname
896: ,scfg.dir[i]->code_suffix
897: ,startup->html_index_file);
898: js_add_file(js_cx
899: ,dir_array
900: ,scfg.dir[i]->sname /* filename */
901: ,scfg.dir[i]->lname /* description */
902: ,NULL /* extdesc */
903: ,getfiles(&scfg,i) /* size */
904: ,0,0,0,0,0 /* unused */
905: ,scfg.dir[i]->misc /* misc */
906: ,scfg.sys_id /* uploader */
907: ,vpath /* link */
908: );
909:
910: }
911: } else if(chk_ar(&scfg,scfg.dir[dir]->ar,user)){
912: SAFEPRINTF(path,"%s*",scfg.dir[dir]->path);
913: glob(path,0,NULL,&g);
914: for(i=0;i<(int)g.gl_pathc;i++) {
915: if(isdir(g.gl_pathv[i]))
916: continue;
917: #ifdef _WIN32
918: GetShortPathName(g.gl_pathv[i], str, sizeof(str));
919: #else
920: SAFECOPY(str,g.gl_pathv[i]);
921: #endif
922: padfname(getfname(str),f.name);
923: f.dir=dir;
924: if(getfileixb(&scfg,&f)) {
925: f.size=0; /* flength(g.gl_pathv[i]); */
926: getfiledat(&scfg,&f);
927: if(f.misc&FM_EXTDESC) {
928: extdesc[0]=0;
929: getextdesc(&scfg, dir, f.datoffset, extdesc);
930: /* Remove Ctrl-A Codes and Ex-ASCII code */
931: remove_ctrl_a(extdesc,NULL);
932: }
933: SAFEPRINTF3(vpath,"/%s/%s/%s"
934: ,scfg.lib[scfg.dir[dir]->lib]->sname
935: ,scfg.dir[dir]->code_suffix
936: ,getfname(g.gl_pathv[i]));
937: js_add_file(js_cx
938: ,file_array
939: ,getfname(g.gl_pathv[i]) /* filename */
940: ,f.desc /* description */
941: ,f.misc&FM_EXTDESC ? extdesc : NULL
942: ,f.size /* size */
943: ,f.cdt /* credits */
944: ,f.date /* time */
945: ,f.dateuled /* uploaded */
946: ,f.datedled /* last downloaded */
947: ,f.timesdled /* times downloaded */
948: ,f.misc /* misc */
949: ,f.uler /* uploader */
950: ,getfname(g.gl_pathv[i]) /* link */
951: );
952: }
953: }
954: globfree(&g);
955: }
956:
957:
958: /* RUN SCRIPT */
959: JS_ClearPendingException(js_cx);
960:
961: if((js_script=JS_CompileFile(js_cx, parent, spath))==NULL) {
962: lprintf(LOG_ERR,"%04d !JavaScript FAILED to compile script (%s)",sock,spath);
963: break;
964: }
965:
966: if((success=JS_ExecuteScript(js_cx, parent, js_script, &rval))!=TRUE) {
967: lprintf(LOG_ERR,"%04d !JavaScript FAILED to execute script (%s)",sock,spath);
968: break;
969: }
970:
971: } while(0);
972:
973:
974: if(js_script!=NULL)
975: JS_DestroyScript(js_cx, js_script);
976:
977: JS_DeleteProperty(js_cx, parent, "path");
978: JS_DeleteProperty(js_cx, parent, "sort");
979: JS_DeleteProperty(js_cx, parent, "reverse");
980: JS_DeleteProperty(js_cx, parent, "file_list");
981: JS_DeleteProperty(js_cx, parent, "dir_list");
982: JS_DeleteProperty(js_cx, parent, "curlib");
983: JS_DeleteProperty(js_cx, parent, "curdir");
984: JS_DeleteProperty(js_cx, parent, "html_index_file");
985:
986: return(success);
987: }
988:
989:
990: #endif /* ifdef JAVASCRIPT */
991:
992:
993: time_t gettimeleft(scfg_t* cfg, user_t* user, time_t starttime)
994: {
995: time_t now;
996: long tleft;
997: time_t timeleft;
998:
999: now=time(NULL);
1000:
1001: if(user->exempt&FLAG('T')) { /* Time online exemption */
1002: timeleft=cfg->level_timepercall[user->level]*60;
1003: if(timeleft<10) /* never get below 10 for exempt users */
1004: timeleft=10; }
1005: else {
1006: tleft=(((long)cfg->level_timeperday[user->level]-user->ttoday)
1007: +user->textra)*60L;
1008: if(tleft<0) tleft=0;
1009: if(tleft>cfg->level_timepercall[user->level]*60)
1010: tleft=cfg->level_timepercall[user->level]*60;
1011: tleft+=user->min*60L;
1012: tleft-=now-starttime;
1013: if(tleft>0x7fffL)
1014: timeleft=0x7fff;
1015: else
1016: timeleft=tleft; }
1017:
1018: return(timeleft);
1019: }
1020:
1021: static time_t checktime(void)
1022: {
1023: struct tm tm;
1024:
1025: memset(&tm,0,sizeof(tm));
1026: tm.tm_year=94;
1027: tm.tm_mday=1;
1028: return(mktime(&tm)-0x2D24BD00L);
1029: }
1030:
1031: BOOL upload_stats(ulong bytes)
1032: {
1033: char str[MAX_PATH+1];
1034: int file;
1035: ulong val;
1036:
1037: sprintf(str,"%sdsts.dab",scfg.ctrl_dir);
1038: if((file=nopen(str,O_RDWR))==-1)
1039: return(FALSE);
1040:
1041: lseek(file,20L,SEEK_SET); /* Skip timestamp, logons and logons today */
1042: read(file,&val,4); /* Uploads today */
1043: val++;
1044: lseek(file,-4L,SEEK_CUR);
1045: write(file,&val,4);
1046: read(file,&val,4); /* Upload bytes today */
1047: val+=bytes;
1048: lseek(file,-4L,SEEK_CUR);
1049: write(file,&val,4);
1050: close(file);
1051: return(TRUE);
1052: }
1053:
1054: BOOL download_stats(ulong bytes)
1055: {
1056: char str[MAX_PATH+1];
1057: int file;
1058: ulong val;
1059:
1060: sprintf(str,"%sdsts.dab",scfg.ctrl_dir);
1061: if((file=nopen(str,O_RDWR))==-1)
1062: return(FALSE);
1063:
1064: lseek(file,28L,SEEK_SET); /* Skip timestamp, logons and logons today */
1065: read(file,&val,4); /* Downloads today */
1066: val++;
1067: lseek(file,-4L,SEEK_CUR);
1068: write(file,&val,4);
1069: read(file,&val,4); /* Download bytes today */
1070: val+=bytes;
1071: lseek(file,-4L,SEEK_CUR);
1072: write(file,&val,4);
1073: close(file);
1074: return(TRUE);
1075: }
1076:
1077: void recverror(SOCKET socket, int rd, int line)
1078: {
1079: if(rd==0)
1080: lprintf(LOG_NOTICE,"%04d Socket closed by peer on receive (line %u)"
1081: ,socket, line);
1082: else if(rd==SOCKET_ERROR) {
1083: if(ERROR_VALUE==ECONNRESET)
1084: lprintf(LOG_NOTICE,"%04d Connection reset by peer on receive (line %u)"
1085: ,socket, line);
1086: else if(ERROR_VALUE==ECONNABORTED)
1087: lprintf(LOG_NOTICE,"%04d Connection aborted by peer on receive (line %u)"
1088: ,socket, line);
1089: else
1090: lprintf(LOG_NOTICE,"%04d !ERROR %d receiving on socket (line %u)"
1091: ,socket, ERROR_VALUE, line);
1092: } else
1093: lprintf(LOG_WARNING,"%04d !ERROR: recv on socket returned unexpected value: %d (line %u)"
1094: ,socket, rd, line);
1095: }
1096:
1097: int sockreadline(SOCKET socket, char* buf, int len, time_t* lastactive)
1098: {
1099: char ch;
1100: int i,rd=0;
1101: fd_set socket_set;
1102: struct timeval tv;
1103:
1104: buf[0]=0;
1105:
1106: if(socket==INVALID_SOCKET) {
1107: lprintf(LOG_WARNING,"INVALID SOCKET in call to sockreadline");
1108: return(0);
1109: }
1110:
1111: while(rd<len-1) {
1112:
1113: tv.tv_sec=startup->max_inactivity;
1114: tv.tv_usec=0;
1115:
1116: FD_ZERO(&socket_set);
1117: FD_SET(socket,&socket_set);
1118:
1119: i=select(socket+1,&socket_set,NULL,NULL,&tv);
1120:
1121: if(server_socket==INVALID_SOCKET || terminate_server) {
1122: sockprintf(socket,"421 Server downed, aborting.");
1123: lprintf(LOG_WARNING,"%04d Server downed, aborting",socket);
1124: return(0);
1125: }
1126: if(i<1) {
1127: if(i==0) {
1128: if((time(NULL)-(*lastactive))>startup->max_inactivity) {
1129: lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",socket);
1130: sockprintf(socket,"421 Disconnecting due to inactivity (%u seconds)."
1131: ,startup->max_inactivity);
1132: return(0);
1133: }
1134: continue;
1135: }
1136: recverror(socket,i,__LINE__);
1137: return(i);
1138: }
1139: #ifdef SOCKET_DEBUG_RECV_CHAR
1140: socket_debug[socket]|=SOCKET_DEBUG_RECV_CHAR;
1141: #endif
1142: i=recv(socket, &ch, 1, 0);
1143: #ifdef SOCKET_DEBUG_RECV_CHAR
1144: socket_debug[socket]&=~SOCKET_DEBUG_RECV_CHAR;
1145: #endif
1146: if(i<1) {
1147: recverror(socket,i,__LINE__);
1148: return(i);
1149: }
1150: if(ch=='\n' /* && rd>=1 */) { /* Mar-9-2003: terminate on sole LF */
1151: break;
1152: }
1153: buf[rd++]=ch;
1154: }
1155: if(rd>0 && buf[rd-1]=='\r')
1156: buf[rd-1]=0;
1157: else
1158: buf[rd]=0;
1159:
1160: return(rd);
1161: }
1162:
1163: #if 0 /* now exported from in xtrn.cpp */
1164: /*****************************************************************************/
1165: /* Returns command line generated from instr with %c replacments */
1166: /*****************************************************************************/
1167: static char* cmdstr(user_t* user, char *instr, char *fpath, char *fspec, char *cmd)
1168: {
1169: char str[256];
1170: int i,j,len;
1171: #ifdef _WIN32
1172: char sfpath[MAX_PATH+1];
1173: #endif
1174:
1175: len=strlen(instr);
1176: for(i=j=0;i<len;i++) {
1177: if(instr[i]=='%') {
1178: i++;
1179: cmd[j]=0;
1180: switch(toupper(instr[i])) {
1181: case 'A': /* User alias */
1182: strcat(cmd,user->alias);
1183: break;
1184: case 'B': /* Baud (DTE) Rate */
1185: case 'C': /* Connect Description */
1186: case 'D': /* Connect (DCE) Rate */
1187: case 'E': /* Estimated Rate */
1188: case 'H': /* Port Handle or Hardware Flow Control */
1189: case 'P': /* COM Port */
1190: case 'R': /* Rows */
1191: case 'T': /* Time left in seconds */
1192: case '&': /* Address of msr */
1193: case 'Y': /* COMSPEC */
1194: /* UNSUPPORTED */
1195: break;
1196: case 'F': /* File path */
1197: strcat(cmd,fpath);
1198: break;
1199: case 'G': /* Temp directory */
1200: strcat(cmd,scfg.temp_dir);
1201: break;
1202: case 'I': /* UART IRQ Line */
1203: strcat(cmd,ultoa(scfg.com_irq,str,10));
1204: break;
1205: case 'J':
1206: strcat(cmd,scfg.data_dir);
1207: break;
1208: case 'K':
1209: strcat(cmd,scfg.ctrl_dir);
1210: break;
1211: case 'L': /* Lines per message */
1212: strcat(cmd,ultoa(scfg.level_linespermsg[user->level],str,10));
1213: break;
1214: case 'M': /* Minutes (credits) for user */
1215: strcat(cmd,ultoa(user->min,str,10));
1216: break;
1217: case 'N': /* Node Directory (same as SBBSNODE environment var) */
1218: strcat(cmd,scfg.node_dir);
1219: break;
1220: case 'O': /* SysOp */
1221: strcat(cmd,scfg.sys_op);
1222: break;
1223: case 'Q': /* QWK ID */
1224: strcat(cmd,scfg.sys_id);
1225: break;
1226: case 'S': /* File Spec */
1227: strcat(cmd,fspec);
1228: break;
1229: case 'U': /* UART I/O Address (in hex) */
1230: strcat(cmd,ultoa(scfg.com_base,str,16));
1231: break;
1232: case 'V': /* Synchronet Version */
1233: sprintf(str,"%s%c",VERSION,REVISION);
1234: break;
1235: case 'W': /* Time-slice API type (mswtype) */
1236: break;
1237: case 'X':
1238: strcat(cmd,scfg.shell[user->shell]->code);
1239: break;
1240: case 'Z':
1241: strcat(cmd,scfg.text_dir);
1242: break;
1243: case '~': /* DOS-compatible (8.3) filename */
1244: #ifdef _WIN32
1245: SAFECOPY(sfpath,fpath);
1246: GetShortPathName(fpath,sfpath,sizeof(sfpath));
1247: strcat(cmd,sfpath);
1248: #else
1249: strcat(cmd,fpath);
1250: #endif
1251: break;
1252: case '!': /* EXEC Directory */
1253: strcat(cmd,scfg.exec_dir);
1254: break;
1255: case '@': /* EXEC Directory for DOS/OS2/Win32, blank for Unix */
1256: #ifndef __unix__
1257: strcat(cmd,scfg.exec_dir);
1258: #endif
1259: break;
1260: case '#': /* Node number (same as SBBSNNUM environment var) */
1261: sprintf(str,"%u",scfg.node_num);
1262: strcat(cmd,str);
1263: break;
1264: case '*':
1265: sprintf(str,"%03u",scfg.node_num);
1266: strcat(cmd,str);
1267: break;
1268: case '$': /* Credits */
1269: strcat(cmd,ultoa(user->cdt+user->freecdt,str,10));
1270: break;
1271: case '%': /* %% for percent sign */
1272: strcat(cmd,"%");
1273: break;
1274: case '?': /* Platform */
1275: #ifdef __OS2__
1276: SAFECOPY(str,"OS2");
1277: #else
1278: SAFECOPY(str,PLATFORM_DESC);
1279: #endif
1280: strlwr(str);
1281: strcat(cmd,str);
1282: break;
1283: default: /* unknown specification */
1284: if(isdigit(instr[i])) {
1285: sprintf(str,"%0*d",instr[i]&0xf,user->number);
1286: strcat(cmd,str); }
1287: break; }
1288: j=strlen(cmd); }
1289: else
1290: cmd[j++]=instr[i]; }
1291: cmd[j]=0;
1292:
1293: return(cmd);
1294: }
1295: #endif
1296:
1297: void DLLCALL ftp_terminate(void)
1298: {
1299: lprintf(LOG_DEBUG,"%04d FTP Server terminate",server_socket);
1300: terminate_server=TRUE;
1301: }
1302:
1303:
1304: typedef struct {
1305: SOCKET ctrl_sock;
1306: SOCKET* data_sock;
1307: BOOL* inprogress;
1308: BOOL* aborted;
1309: BOOL delfile;
1310: BOOL tmpfile;
1311: BOOL credits;
1312: BOOL append;
1313: long filepos;
1314: char filename[MAX_PATH+1];
1315: time_t* lastactive;
1316: user_t* user;
1317: int dir;
1318: char* desc;
1319: } xfer_t;
1320:
1321: static void send_thread(void* arg)
1322: {
1323: char buf[8192];
1324: char fname[MAX_PATH+1];
1325: char str[128];
1326: char tmp[128];
1327: char username[128];
1328: int i;
1329: int rd;
1330: int wr;
1331: long mod;
1332: ulong l;
1333: ulong total=0;
1334: ulong last_total=0;
1335: ulong dur;
1336: ulong cps;
1337: ulong length;
1338: BOOL error=FALSE;
1339: FILE* fp;
1340: file_t f;
1341: xfer_t xfer;
1342: time_t now;
1343: time_t start;
1344: time_t last_report;
1345: user_t uploader;
1346: SOCKADDR_IN addr;
1347: socklen_t addr_len;
1348: fd_set socket_set;
1349: struct timeval tv;
1350:
1351: xfer=*(xfer_t*)arg;
1352: free(arg);
1353:
1354: thread_up(TRUE /* setuid */);
1355:
1356: length=flength(xfer.filename);
1357:
1358: if((fp=fnopen(NULL,xfer.filename,O_RDONLY|O_BINARY))==NULL /* non-shareable open failed */
1359: && (fp=fopen(xfer.filename,"rb"))==NULL) { /* shareable open failed */
1360: lprintf(LOG_ERR,"%04d !DATA ERROR %d opening %s",xfer.ctrl_sock,errno,xfer.filename);
1361: sockprintf(xfer.ctrl_sock,"450 ERROR %d opening %s.",errno,xfer.filename);
1362: if(xfer.tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES))
1363: remove(xfer.filename);
1364: ftp_close_socket(xfer.data_sock,__LINE__);
1365: *xfer.inprogress=FALSE;
1366: thread_down();
1367: return;
1368: }
1369:
1370: #ifdef SOCKET_DEBUG_SENDTHREAD
1371: socket_debug[xfer.ctrl_sock]|=SOCKET_DEBUG_SENDTHREAD;
1372: #endif
1373:
1374: *xfer.aborted=FALSE;
1375: if(startup->options&FTP_OPT_DEBUG_DATA || xfer.filepos)
1376: lprintf(LOG_DEBUG,"%04d DATA socket %d sending %s from offset %lu"
1377: ,xfer.ctrl_sock,*xfer.data_sock,xfer.filename,xfer.filepos);
1378:
1379: fseek(fp,xfer.filepos,SEEK_SET);
1380: last_report=start=time(NULL);
1381: while((xfer.filepos+total)<length) {
1382:
1383: now=time(NULL);
1384:
1385: /* Periodic progress report */
1386: if(total && now>=last_report+XFER_REPORT_INTERVAL) {
1387: if(xfer.filepos)
1388: sprintf(str," from offset %lu",xfer.filepos);
1389: else
1390: str[0]=0;
1391: lprintf(LOG_INFO,"%04d Sent %lu bytes (%lu total) of %s (%lu cps)%s"
1392: ,xfer.ctrl_sock,total,length,xfer.filename
1393: ,(total-last_total)/(now-last_report)
1394: ,str);
1395: last_total=total;
1396: last_report=now;
1397: }
1398:
1399: if(*xfer.aborted==TRUE) {
1400: lprintf(LOG_WARNING,"%04d !DATA Transfer aborted",xfer.ctrl_sock);
1401: sockprintf(xfer.ctrl_sock,"426 Transfer aborted.");
1402: error=TRUE;
1403: break;
1404: }
1405: if(server_socket==INVALID_SOCKET || terminate_server) {
1406: lprintf(LOG_WARNING,"%04d !DATA Transfer locally aborted",xfer.ctrl_sock);
1407: sockprintf(xfer.ctrl_sock,"426 Transfer locally aborted.");
1408: error=TRUE;
1409: break;
1410: }
1411:
1412: /* Check socket for writability (using select) */
1413: tv.tv_sec=1;
1414: tv.tv_usec=0;
1415:
1416: FD_ZERO(&socket_set);
1417: FD_SET(*xfer.data_sock,&socket_set);
1418:
1419: i=select((*xfer.data_sock)+1,NULL,&socket_set,NULL,&tv);
1420: if(i==SOCKET_ERROR) {
1421: lprintf(LOG_WARNING,"%04d !DATA ERROR %d selecting socket %d for send"
1422: ,xfer.ctrl_sock, ERROR_VALUE, *xfer.data_sock);
1423: sockprintf(xfer.ctrl_sock,"426 Transfer error.");
1424: error=TRUE;
1425: break;
1426: }
1427: if(i<1)
1428: continue;
1429:
1430: fseek(fp,xfer.filepos+total,SEEK_SET);
1431: rd=fread(buf,sizeof(char),sizeof(buf),fp);
1432: if(rd<1) /* EOF or READ error */
1433: break;
1434:
1435: #ifdef SOCKET_DEBUG_SEND
1436: socket_debug[xfer.ctrl_sock]|=SOCKET_DEBUG_SEND;
1437: #endif
1438: wr=sendsocket(*xfer.data_sock,buf,rd);
1439: #ifdef SOCKET_DEBUG_SEND
1440: socket_debug[xfer.ctrl_sock]&=~SOCKET_DEBUG_SEND;
1441: #endif
1442: if(wr<1) {
1443: if(wr==SOCKET_ERROR) {
1444: if(ERROR_VALUE==EWOULDBLOCK) {
1445: /*lprintf(LOG_WARNING,"%04d DATA send would block, retrying",xfer.ctrl_sock);*/
1446: YIELD();
1447: continue;
1448: }
1449: else if(ERROR_VALUE==ECONNRESET)
1450: lprintf(LOG_WARNING,"%04d DATA Connection reset by peer, sending on socket %d"
1451: ,xfer.ctrl_sock,*xfer.data_sock);
1452: else if(ERROR_VALUE==ECONNABORTED)
1453: lprintf(LOG_WARNING,"%04d DATA Connection aborted by peer, sending on socket %d"
1454: ,xfer.ctrl_sock,*xfer.data_sock);
1455: else
1456: lprintf(LOG_WARNING,"%04d !DATA ERROR %d sending on data socket %d"
1457: ,xfer.ctrl_sock,ERROR_VALUE,*xfer.data_sock);
1458: /* Send NAK */
1459: sockprintf(xfer.ctrl_sock,"426 Error %d sending on DATA channel"
1460: ,ERROR_VALUE);
1461: error=TRUE;
1462: break;
1463: }
1464: if(wr==0) {
1465: lprintf(LOG_WARNING,"%04d !DATA socket %d disconnected",xfer.ctrl_sock, *xfer.data_sock);
1466: sockprintf(xfer.ctrl_sock,"426 DATA channel disconnected");
1467: error=TRUE;
1468: break;
1469: }
1470: lprintf(LOG_ERR,"%04d !DATA SEND ERROR %d (%d) on socket %d"
1471: ,xfer.ctrl_sock, wr, ERROR_VALUE, *xfer.data_sock);
1472: sockprintf(xfer.ctrl_sock,"451 DATA send error");
1473: error=TRUE;
1474: break;
1475: }
1476: total+=wr;
1477: *xfer.lastactive=time(NULL);
1478: YIELD();
1479: }
1480:
1481: if((i=ferror(fp))!=0)
1482: lprintf(LOG_ERR,"%04d !FILE ERROR %d (%d)",xfer.ctrl_sock,i,errno);
1483:
1484: ftp_close_socket(xfer.data_sock,__LINE__); /* Signal end of file */
1485: if(startup->options&FTP_OPT_DEBUG_DATA)
1486: lprintf(LOG_DEBUG,"%04d DATA socket closed",xfer.ctrl_sock);
1487:
1488: if(!error) {
1489: dur=time(NULL)-start;
1490: cps=dur ? total/dur : total*2;
1491: lprintf(LOG_INFO,"%04d Transfer successful: %lu bytes sent in %lu seconds (%lu cps)"
1492: ,xfer.ctrl_sock
1493: ,total,dur,cps);
1494: sockprintf(xfer.ctrl_sock,"226 Download complete (%lu cps).",cps);
1495:
1496: if(xfer.dir>=0) {
1497: memset(&f,0,sizeof(f));
1498: #ifdef _WIN32
1499: GetShortPathName(xfer.filename,fname,sizeof(fname));
1500: #else
1501: SAFECOPY(fname,xfer.filename);
1502: #endif
1503: padfname(getfname(fname),f.name);
1504: f.dir=xfer.dir;
1505: f.size=total;
1506: if(getfileixb(&scfg,&f)==TRUE && getfiledat(&scfg,&f)==TRUE) {
1507: f.timesdled++;
1508: putfiledat(&scfg,&f);
1509: f.datedled=time(NULL);
1510: putfileixb(&scfg,&f);
1511:
1512: lprintf(LOG_INFO,"%04d %s downloaded: %s (%lu times total)"
1513: ,xfer.ctrl_sock
1514: ,xfer.user->alias
1515: ,xfer.filename
1516: ,f.timesdled);
1517: /**************************/
1518: /* Update Uploader's Info */
1519: /**************************/
1520: uploader.number=matchuser(&scfg,f.uler,TRUE /*sysop_alias*/);
1521: if(uploader.number
1522: && uploader.number!=xfer.user->number
1523: && getuserdat(&scfg,&uploader)==0
1524: && uploader.firston<f.dateuled) {
1525: l=f.cdt;
1526: if(!(scfg.dir[f.dir]->misc&DIR_CDTDL)) /* Don't give credits on d/l */
1527: l=0;
1528: if(scfg.dir[f.dir]->misc&DIR_CDTMIN && cps) { /* Give min instead of cdt */
1529: mod=((ulong)(l*(scfg.dir[f.dir]->dn_pct/100.0))/cps)/60;
1530: adjustuserrec(&scfg,uploader.number,U_MIN,10,mod);
1531: sprintf(tmp,"%lu minute",mod);
1532: } else {
1533: mod=(ulong)(l*(scfg.dir[f.dir]->dn_pct/100.0));
1534: adjustuserrec(&scfg,uploader.number,U_CDT,10,mod);
1535: ultoac(mod,tmp);
1536: }
1537: if(!(scfg.dir[f.dir]->misc&DIR_QUIET)) {
1538: addr_len = sizeof(addr);
1539: if(uploader.level>=SYSOP_LEVEL
1540: && getpeername(xfer.ctrl_sock,(struct sockaddr *)&addr,&addr_len)==0)
1541: SAFEPRINTF2(username,"%s [%s]",xfer.user->alias,inet_ntoa(addr.sin_addr));
1542: else
1543: SAFECOPY(username,xfer.user->alias);
1544: /* Inform uploader of downloaded file */
1545: safe_snprintf(str,sizeof(str),text[DownloadUserMsg]
1546: ,getfname(xfer.filename)
1547: ,xfer.filepos ? "partially FTP-" : "FTP-"
1548: ,username,tmp);
1549: putsmsg(&scfg,uploader.number,str);
1550: }
1551: }
1552: }
1553: if(!xfer.tmpfile && !xfer.delfile && !(scfg.dir[f.dir]->misc&DIR_NOSTAT))
1554: download_stats(total);
1555: }
1556:
1557: if(xfer.credits) {
1558: user_downloaded(&scfg, xfer.user, 1, total);
1559: if(xfer.dir>=0 && !is_download_free(&scfg,xfer.dir,xfer.user))
1560: subtract_cdt(&scfg, xfer.user, xfer.credits);
1561: }
1562: }
1563:
1564: fclose(fp);
1565: if(server_socket!=INVALID_SOCKET && !terminate_server)
1566: *xfer.inprogress=FALSE;
1567: if(xfer.tmpfile) {
1568: if(!(startup->options&FTP_OPT_KEEP_TEMP_FILES))
1569: remove(xfer.filename);
1570: }
1571: else if(xfer.delfile && !error)
1572: remove(xfer.filename);
1573:
1574: #if defined(SOCKET_DEBUG_SENDTHREAD)
1575: socket_debug[xfer.ctrl_sock]&=~SOCKET_DEBUG_SENDTHREAD;
1576: #endif
1577:
1578: thread_down();
1579: }
1580:
1581: static void receive_thread(void* arg)
1582: {
1583: char* p;
1584: char str[128];
1585: char buf[8192];
1586: char ext[F_EXBSIZE+1];
1587: char desc[F_EXBSIZE+1];
1588: char cmd[MAX_PATH*2];
1589: char tmp[MAX_PATH+1];
1590: char fname[MAX_PATH+1];
1591: int i;
1592: int rd;
1593: int file;
1594: ulong total=0;
1595: ulong last_total=0;
1596: ulong dur;
1597: ulong cps;
1598: BOOL error=FALSE;
1599: BOOL filedat;
1600: FILE* fp;
1601: file_t f;
1602: xfer_t xfer;
1603: time_t now;
1604: time_t start;
1605: time_t last_report;
1606: fd_set socket_set;
1607: struct timeval tv;
1608:
1609: xfer=*(xfer_t*)arg;
1610: free(arg);
1611:
1612: thread_up(TRUE /* setuid */);
1613:
1614: if((fp=fopen(xfer.filename,xfer.append ? "ab" : "wb"))==NULL) {
1615: lprintf(LOG_ERR,"%04d !DATA ERROR %d opening %s",xfer.ctrl_sock,errno,xfer.filename);
1616: sockprintf(xfer.ctrl_sock,"450 ERROR %d opening %s.",errno,xfer.filename);
1617: ftp_close_socket(xfer.data_sock,__LINE__);
1618: *xfer.inprogress=FALSE;
1619: thread_down();
1620: return;
1621: }
1622:
1623: if(xfer.append)
1624: xfer.filepos=filelength(fileno(fp));
1625:
1626: *xfer.aborted=FALSE;
1627: if(xfer.filepos || startup->options&FTP_OPT_DEBUG_DATA)
1628: lprintf(LOG_DEBUG,"%04d DATA socket %d receiving %s from offset %lu"
1629: ,xfer.ctrl_sock,*xfer.data_sock,xfer.filename,xfer.filepos);
1630:
1631: fseek(fp,xfer.filepos,SEEK_SET);
1632: last_report=start=time(NULL);
1633: while(1) {
1634:
1635: now=time(NULL);
1636:
1637: /* Periodic progress report */
1638: if(total && now>=last_report+XFER_REPORT_INTERVAL) {
1639: if(xfer.filepos)
1640: sprintf(str," from offset %lu",xfer.filepos);
1641: else
1642: str[0]=0;
1643: lprintf(LOG_INFO,"%04d Received %lu bytes of %s (%lu cps)%s"
1644: ,xfer.ctrl_sock,total,xfer.filename
1645: ,(total-last_total)/(now-last_report)
1646: ,str);
1647: last_total=total;
1648: last_report=now;
1649: }
1650: if(*xfer.aborted==TRUE) {
1651: lprintf(LOG_WARNING,"%04d !DATA Transfer aborted",xfer.ctrl_sock);
1652: /* Send NAK */
1653: sockprintf(xfer.ctrl_sock,"426 Transfer aborted.");
1654: error=TRUE;
1655: break;
1656: }
1657: if(server_socket==INVALID_SOCKET || terminate_server) {
1658: lprintf(LOG_WARNING,"%04d !DATA Transfer locally aborted",xfer.ctrl_sock);
1659: /* Send NAK */
1660: sockprintf(xfer.ctrl_sock,"426 Transfer locally aborted.");
1661: error=TRUE;
1662: break;
1663: }
1664:
1665: /* Check socket for readability (using select) */
1666: tv.tv_sec=1;
1667: tv.tv_usec=0;
1668:
1669: FD_ZERO(&socket_set);
1670: FD_SET(*xfer.data_sock,&socket_set);
1671:
1672: i=select((*xfer.data_sock)+1,&socket_set,NULL,NULL,&tv);
1673: if(i==SOCKET_ERROR) {
1674: lprintf(LOG_WARNING,"%04d !DATA ERROR %d selecting socket %d for receive"
1675: ,xfer.ctrl_sock, ERROR_VALUE, *xfer.data_sock);
1676: sockprintf(xfer.ctrl_sock,"426 Transfer error.");
1677: error=TRUE;
1678: break;
1679: }
1680: if(i<1)
1681: continue;
1682:
1683: #if defined(SOCKET_DEBUG_RECV_BUF)
1684: socket_debug[xfer.ctrl_sock]|=SOCKET_DEBUG_RECV_BUF;
1685: #endif
1686: rd=recv(*xfer.data_sock,buf,sizeof(buf),0);
1687: #if defined(SOCKET_DEBUG_RECV_BUF)
1688: socket_debug[xfer.ctrl_sock]&=~SOCKET_DEBUG_RECV_BUF;
1689: #endif
1690: if(rd<1) {
1691: if(rd==0) { /* Socket closed */
1692: if(startup->options&FTP_OPT_DEBUG_DATA)
1693: lprintf(LOG_DEBUG,"%04d DATA socket %d closed by client"
1694: ,xfer.ctrl_sock,*xfer.data_sock);
1695: break;
1696: }
1697: if(rd==SOCKET_ERROR) {
1698: if(ERROR_VALUE==EWOULDBLOCK) {
1699: /*lprintf(LOG_WARNING,"%04d DATA recv would block, retrying",xfer.ctrl_sock);*/
1700: YIELD();
1701: continue;
1702: }
1703: else if(ERROR_VALUE==ECONNRESET)
1704: lprintf(LOG_WARNING,"%04d DATA Connection reset by peer, receiving on socket %d"
1705: ,xfer.ctrl_sock,*xfer.data_sock);
1706: else if(ERROR_VALUE==ECONNABORTED)
1707: lprintf(LOG_WARNING,"%04d DATA Connection aborted by peer, receiving on socket %d"
1708: ,xfer.ctrl_sock,*xfer.data_sock);
1709: else
1710: lprintf(LOG_WARNING,"%04d !DATA ERROR %d receiving on data socket %d"
1711: ,xfer.ctrl_sock,ERROR_VALUE,*xfer.data_sock);
1712: /* Send NAK */
1713: sockprintf(xfer.ctrl_sock,"426 Error %d receiving on DATA channel"
1714: ,ERROR_VALUE);
1715: error=TRUE;
1716: break;
1717: }
1718: lprintf(LOG_ERR,"%04d !DATA ERROR recv returned %d on socket %d"
1719: ,xfer.ctrl_sock,rd,*xfer.data_sock);
1720: /* Send NAK */
1721: sockprintf(xfer.ctrl_sock,"451 Unexpected socket error: %d",rd);
1722: error=TRUE;
1723: break;
1724: }
1725: fwrite(buf,1,rd,fp);
1726: total+=rd;
1727: *xfer.lastactive=time(NULL);
1728: YIELD();
1729: }
1730:
1731: if(server_socket!=INVALID_SOCKET && !terminate_server)
1732: *xfer.inprogress=FALSE;
1733: fclose(fp);
1734:
1735: ftp_close_socket(xfer.data_sock,__LINE__);
1736: if(error && startup->options&FTP_OPT_DEBUG_DATA)
1737: lprintf(LOG_DEBUG,"%04d DATA socket %d closed",xfer.ctrl_sock,*xfer.data_sock);
1738:
1739: if(!error) {
1740: dur=time(NULL)-start;
1741: cps=dur ? total/dur : total*2;
1742: lprintf(LOG_INFO,"%04d Transfer successful: %lu bytes received in %lu seconds (%lu cps)"
1743: ,xfer.ctrl_sock
1744: ,total,dur,cps);
1745:
1746: if(xfer.dir>=0) {
1747: memset(&f,0,sizeof(f));
1748: #ifdef _WIN32
1749: GetShortPathName(xfer.filename,fname,sizeof(fname));
1750: #else
1751: SAFECOPY(fname,xfer.filename);
1752: #endif
1753: padfname(getfname(fname),f.name);
1754: f.dir=xfer.dir;
1755: filedat=getfileixb(&scfg,&f);
1756: if(scfg.dir[f.dir]->misc&DIR_AONLY) /* Forced anonymous */
1757: f.misc|=FM_ANON;
1758: f.cdt=flength(xfer.filename);
1759: f.dateuled=time(NULL);
1760:
1761: /* Desciption specified with DESC command? */
1762: if(xfer.desc!=NULL && *xfer.desc!=0)
1763: SAFECOPY(f.desc,xfer.desc);
1764:
1765: /* Necessary for DIR and LIB ARS keyword support in subsequent chk_ar()'s */
1766: SAFECOPY(xfer.user->curdir, scfg.dir[f.dir]->code);
1767:
1768: /* FILE_ID.DIZ support */
1769: p=strrchr(f.name,'.');
1770: if(p!=NULL && scfg.dir[f.dir]->misc&DIR_DIZ) {
1771: for(i=0;i<scfg.total_fextrs;i++)
1772: if(!stricmp(scfg.fextr[i]->ext,p+1)
1773: && chk_ar(&scfg,scfg.fextr[i]->ar,xfer.user))
1774: break;
1775: if(i<scfg.total_fextrs) {
1776: sprintf(tmp,"%sFILE_ID.DIZ",scfg.temp_dir);
1777: if(fexistcase(tmp))
1778: remove(tmp);
1779: system(cmdstr(&scfg,xfer.user,scfg.fextr[i]->cmd,fname,"FILE_ID.DIZ",cmd));
1780: if(!fexistcase(tmp)) {
1781: sprintf(tmp,"%sDESC.SDI",scfg.temp_dir);
1782: if(fexistcase(tmp))
1783: remove(tmp);
1784: system(cmdstr(&scfg,xfer.user,scfg.fextr[i]->cmd,fname,"DESC.SDI",cmd));
1785: fexistcase(tmp); /* fixes filename case */
1786: }
1787: if((file=nopen(tmp,O_RDONLY))!=-1) {
1788: memset(ext,0,sizeof(ext));
1789: read(file,ext,sizeof(ext)-1);
1790: for(i=sizeof(ext)-1;i;i--) /* trim trailing spaces */
1791: if(ext[i-1]>' ')
1792: break;
1793: ext[i]=0;
1794: if(!f.desc[0]) { /* use for normal description */
1795: SAFECOPY(desc,ext);
1796: strip_exascii(desc); /* strip extended ASCII chars */
1797: prep_file_desc(desc); /* strip control chars and dupe chars */
1798: for(i=0;desc[i];i++) /* find approprate first char */
1799: if(isalnum(desc[i]))
1800: break;
1801: SAFECOPY(f.desc,desc+i);
1802: }
1803: close(file);
1804: remove(tmp);
1805: f.misc|=FM_EXTDESC;
1806: }
1807: }
1808: } /* FILE_ID.DIZ support */
1809:
1810: if(f.desc[0]==0) /* no description given, use (long) filename */
1811: SAFECOPY(f.desc,getfname(xfer.filename));
1812:
1813: SAFECOPY(f.uler,xfer.user->alias); /* exception here, Aug-27-2002 */
1814: if(filedat) {
1815: if(!putfiledat(&scfg,&f))
1816: lprintf(LOG_ERR,"%04d !ERROR updating file (%s) in database",xfer.ctrl_sock,f.name);
1817: /* need to update the index here */
1818: } else {
1819: if(!addfiledat(&scfg,&f))
1820: lprintf(LOG_ERR,"%04d !ERROR adding file (%s) to database",xfer.ctrl_sock,f.name);
1821: }
1822:
1823: if(f.misc&FM_EXTDESC)
1824: putextdesc(&scfg,f.dir,f.datoffset,ext);
1825:
1826: if(scfg.dir[f.dir]->upload_sem[0])
1827: ftouch(scfg.dir[f.dir]->upload_sem);
1828: /**************************/
1829: /* Update Uploader's Info */
1830: /**************************/
1831: user_uploaded(&scfg, xfer.user, (!xfer.append && xfer.filepos==0) ? 1:0, total);
1832: if(scfg.dir[f.dir]->up_pct && scfg.dir[f.dir]->misc&DIR_CDTUL) { /* credit for upload */
1833: if(scfg.dir[f.dir]->misc&DIR_CDTMIN && cps) /* Give min instead of cdt */
1834: xfer.user->min=adjustuserrec(&scfg,xfer.user->number,U_MIN,10
1835: ,((ulong)(total*(scfg.dir[f.dir]->up_pct/100.0))/cps)/60);
1836: else
1837: xfer.user->cdt=adjustuserrec(&scfg,xfer.user->number,U_CDT,10
1838: ,(ulong)(f.cdt*(scfg.dir[f.dir]->up_pct/100.0)));
1839: }
1840: if(!(scfg.dir[f.dir]->misc&DIR_NOSTAT))
1841: upload_stats(total);
1842: }
1843: /* Send ACK */
1844: sockprintf(xfer.ctrl_sock,"226 Upload complete (%lu cps).",cps);
1845: }
1846:
1847: thread_down();
1848: }
1849:
1850:
1851:
1852: static void filexfer(SOCKADDR_IN* addr, SOCKET ctrl_sock, SOCKET pasv_sock, SOCKET* data_sock
1853: ,char* filename, long filepos, BOOL* inprogress, BOOL* aborted
1854: ,BOOL delfile, BOOL tmpfile
1855: ,time_t* lastactive
1856: ,user_t* user
1857: ,int dir
1858: ,BOOL receiving
1859: ,BOOL credits
1860: ,BOOL append
1861: ,char* desc)
1862: {
1863: int result;
1864: ulong l;
1865: socklen_t addr_len;
1866: SOCKADDR_IN server_addr;
1867: BOOL reuseaddr;
1868: xfer_t* xfer;
1869: struct timeval tv;
1870: fd_set socket_set;
1871:
1872: if((*inprogress)==TRUE) {
1873: lprintf(LOG_WARNING,"%04d !TRANSFER already in progress",ctrl_sock);
1874: sockprintf(ctrl_sock,"425 Transfer already in progress.");
1875: return;
1876: }
1877: *inprogress=TRUE;
1878:
1879: if(*data_sock!=INVALID_SOCKET)
1880: ftp_close_socket(data_sock,__LINE__);
1881:
1882: if(pasv_sock==INVALID_SOCKET) { /* !PASV */
1883:
1884: if((*data_sock=socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) == INVALID_SOCKET) {
1885: lprintf(LOG_ERR,"%04d !DATA ERROR %d opening socket", ctrl_sock, ERROR_VALUE);
1886: sockprintf(ctrl_sock,"425 Error %d opening socket",ERROR_VALUE);
1887: if(tmpfile)
1888: remove(filename);
1889: *inprogress=FALSE;
1890: return;
1891: }
1892: if(startup->socket_open!=NULL)
1893: startup->socket_open(startup->cbdata,TRUE);
1894: sockets++;
1895: if(startup->options&FTP_OPT_DEBUG_DATA)
1896: lprintf(LOG_DEBUG,"%04d DATA socket %d opened",ctrl_sock,*data_sock);
1897:
1898: /* Use port-1 for all data connections */
1899: reuseaddr=TRUE;
1900: setsockopt(*data_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&reuseaddr,sizeof(reuseaddr));
1901:
1902: memset(&server_addr, 0, sizeof(server_addr));
1903:
1904: server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
1905: server_addr.sin_family = AF_INET;
1906: server_addr.sin_port = htons((WORD)(startup->port-1)); /* 20? */
1907:
1908: result=bind(*data_sock, (struct sockaddr *) &server_addr,sizeof(server_addr));
1909: if(result!=0) {
1910: server_addr.sin_port = 0; /* any user port */
1911: result=bind(*data_sock, (struct sockaddr *) &server_addr,sizeof(server_addr));
1912: }
1913: if(result!=0) {
1914: lprintf(LOG_ERR,"%04d !DATA ERROR %d (%d) binding socket %d"
1915: ,ctrl_sock, result, ERROR_VALUE, *data_sock);
1916: sockprintf(ctrl_sock,"425 Error %d binding socket",ERROR_VALUE);
1917: if(tmpfile)
1918: remove(filename);
1919: *inprogress=FALSE;
1920: ftp_close_socket(data_sock,__LINE__);
1921: return;
1922: }
1923:
1924: result=connect(*data_sock, (struct sockaddr *)addr,sizeof(struct sockaddr));
1925: if(result!=0) {
1926: lprintf(LOG_WARNING,"%04d !DATA ERROR %d (%d) connecting to client %s port %u on socket %d"
1927: ,ctrl_sock,result,ERROR_VALUE
1928: ,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port),*data_sock);
1929: sockprintf(ctrl_sock,"425 Error %d connecting to socket",ERROR_VALUE);
1930: if(tmpfile)
1931: remove(filename);
1932: *inprogress=FALSE;
1933: ftp_close_socket(data_sock,__LINE__);
1934: return;
1935: }
1936: if(startup->options&FTP_OPT_DEBUG_DATA)
1937: lprintf(LOG_DEBUG,"%04d DATA socket %d connected to %s port %u"
1938: ,ctrl_sock,*data_sock,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port));
1939:
1940: } else { /* PASV */
1941:
1942: if(startup->options&FTP_OPT_DEBUG_DATA) {
1943: addr_len=sizeof(SOCKADDR_IN);
1944: if((result=getsockname(pasv_sock, (struct sockaddr *)addr,&addr_len))!=0)
1945: lprintf(LOG_ERR,"%04d !ERROR %d (%d) getting address/port of passive socket (%u)"
1946: ,ctrl_sock,result,ERROR_VALUE,pasv_sock);
1947: else
1948: lprintf(LOG_DEBUG,"%04d PASV DATA socket %d listening on %s port %u"
1949: ,ctrl_sock,pasv_sock,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port));
1950: }
1951:
1952: /* Setup for select() */
1953: tv.tv_sec=TIMEOUT_SOCKET_LISTEN;
1954: tv.tv_usec=0;
1955:
1956: FD_ZERO(&socket_set);
1957: FD_SET(pasv_sock,&socket_set);
1958:
1959: #if defined(SOCKET_DEBUG_SELECT)
1960: socket_debug[ctrl_sock]|=SOCKET_DEBUG_SELECT;
1961: #endif
1962: result=select(pasv_sock+1,&socket_set,NULL,NULL,&tv);
1963: #if defined(SOCKET_DEBUG_SELECT)
1964: socket_debug[ctrl_sock]&=~SOCKET_DEBUG_SELECT;
1965: #endif
1966: if(result<1) {
1967: lprintf(LOG_WARNING,"%04d !PASV select returned %d (error: %d)",ctrl_sock,result,ERROR_VALUE);
1968: sockprintf(ctrl_sock,"425 Error %d selecting socket for connection",ERROR_VALUE);
1969: if(tmpfile)
1970: remove(filename);
1971: *inprogress=FALSE;
1972: return;
1973: }
1974:
1975: addr_len=sizeof(SOCKADDR_IN);
1976: #ifdef SOCKET_DEBUG_ACCEPT
1977: socket_debug[ctrl_sock]|=SOCKET_DEBUG_ACCEPT;
1978: #endif
1979: *data_sock=accept(pasv_sock,(struct sockaddr*)addr,&addr_len);
1980: #ifdef SOCKET_DEBUG_ACCEPT
1981: socket_debug[ctrl_sock]&=~SOCKET_DEBUG_ACCEPT;
1982: #endif
1983: if(*data_sock==INVALID_SOCKET) {
1984: lprintf(LOG_WARNING,"%04d !PASV DATA ERROR %d accepting connection on socket %d"
1985: ,ctrl_sock,ERROR_VALUE,pasv_sock);
1986: sockprintf(ctrl_sock,"425 Error %d accepting connection",ERROR_VALUE);
1987: if(tmpfile)
1988: remove(filename);
1989: *inprogress=FALSE;
1990: return;
1991: }
1992: if(startup->socket_open!=NULL)
1993: startup->socket_open(startup->cbdata,TRUE);
1994: sockets++;
1995: if(startup->options&FTP_OPT_DEBUG_DATA)
1996: lprintf(LOG_DEBUG,"%04d PASV DATA socket %d connected to %s port %u"
1997: ,ctrl_sock,*data_sock,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port));
1998: }
1999:
2000: do {
2001:
2002: l=1;
2003:
2004: if(ioctlsocket(*data_sock, FIONBIO, &l)!=0) {
2005: lprintf(LOG_ERR,"%04d !DATA ERROR %d disabling socket blocking"
2006: ,ctrl_sock, ERROR_VALUE);
2007: sockprintf(ctrl_sock,"425 Error %d disabling socket blocking"
2008: ,ERROR_VALUE);
2009: break;
2010: }
2011:
2012: if((xfer=malloc(sizeof(xfer_t)))==NULL) {
2013: lprintf(LOG_CRIT,"%04d !MALLOC FAILURE LINE %d",ctrl_sock,__LINE__);
2014: sockprintf(ctrl_sock,"425 MALLOC FAILURE");
2015: break;
2016: }
2017: memset(xfer,0,sizeof(xfer_t));
2018: xfer->ctrl_sock=ctrl_sock;
2019: xfer->data_sock=data_sock;
2020: xfer->inprogress=inprogress;
2021: xfer->aborted=aborted;
2022: xfer->delfile=delfile;
2023: xfer->tmpfile=tmpfile;
2024: xfer->append=append;
2025: xfer->filepos=filepos;
2026: xfer->credits=credits;
2027: xfer->lastactive=lastactive;
2028: xfer->user=user;
2029: xfer->dir=dir;
2030: xfer->desc=desc;
2031: SAFECOPY(xfer->filename,filename);
2032: if(receiving)
2033: result=_beginthread(receive_thread,0,(void*)xfer);
2034: else
2035: result=_beginthread(send_thread,0,(void*)xfer);
2036:
2037: if(result!=-1)
2038: return; /* success */
2039:
2040: } while(0);
2041:
2042: /* failure */
2043: if(tmpfile)
2044: remove(filename);
2045: *inprogress=FALSE;
2046: }
2047:
2048: /* convert "user name" to "user.name" or "mr. user" to "mr._user" */
2049: char* dotname(char* in, char* out)
2050: {
2051: char ch;
2052: int i;
2053:
2054: if(strchr(in,'.')==NULL)
2055: ch='.';
2056: else
2057: ch='_';
2058: for(i=0;in[i];i++)
2059: if(in[i]<=' ')
2060: out[i]=ch;
2061: else
2062: out[i]=in[i];
2063: out[i]=0;
2064: return(out);
2065: }
2066:
2067: void parsepath(char** pp, user_t* user, int* curlib, int* curdir)
2068: {
2069: char* p;
2070: char* tp;
2071: char path[MAX_PATH+1];
2072: int dir=*curdir;
2073: int lib=*curlib;
2074:
2075: SAFECOPY(path,*pp);
2076: p=path;
2077:
2078: if(*p=='/') {
2079: p++;
2080: lib=-1;
2081: }
2082: else if(!strncmp(p,"./",2))
2083: p+=2;
2084:
2085: if(!strncmp(p,"..",2)) {
2086: p+=2;
2087: if(dir>=0)
2088: dir=-1;
2089: else if(lib>=0)
2090: lib=-1;
2091: if(*p=='/')
2092: p++;
2093: }
2094:
2095: if(*p==0) {
2096: *curlib=lib;
2097: *curdir=dir;
2098: return;
2099: }
2100:
2101: if(lib<0) { /* root */
2102: tp=strchr(p,'/');
2103: if(tp) *tp=0;
2104: for(lib=0;lib<scfg.total_libs;lib++) {
2105: if(!chk_ar(&scfg,scfg.lib[lib]->ar,user))
2106: continue;
2107: if(!stricmp(scfg.lib[lib]->sname,p))
2108: break;
2109: }
2110: if(lib>=scfg.total_libs) { /* not found */
2111: *curlib=-1;
2112: return;
2113: }
2114: *curlib=lib;
2115:
2116: if(tp==NULL) {
2117: *curdir=-1;
2118: return;
2119: }
2120:
2121: p=tp+1;
2122: }
2123:
2124: tp=strchr(p,'/');
2125: if(tp!=NULL) {
2126: *tp=0;
2127: tp++;
2128: } else
2129: tp=p+strlen(p);
2130:
2131: for(dir=0;dir<scfg.total_dirs;dir++) {
2132: if(scfg.dir[dir]->lib!=lib)
2133: continue;
2134: if(dir!=scfg.sysop_dir && dir!=scfg.upload_dir
2135: && !chk_ar(&scfg,scfg.dir[dir]->ar,user))
2136: continue;
2137: if(!stricmp(scfg.dir[dir]->code_suffix,p))
2138: break;
2139: }
2140:
2141: if(dir>=scfg.total_dirs) /* not found */
2142: return;
2143:
2144: *curdir=dir;
2145:
2146: *pp+=tp-path; /* skip "lib/dir/" */
2147: }
2148:
2149: static BOOL ftpalias(char* fullalias, char* filename, user_t* user, int* curdir)
2150: {
2151: char* p;
2152: char* tp;
2153: char* fname="";
2154: char line[512];
2155: char alias[512];
2156: char aliasfile[MAX_PATH+1];
2157: int dir=-1;
2158: FILE* fp;
2159: BOOL result=FALSE;
2160:
2161: sprintf(aliasfile,"%sftpalias.cfg",scfg.ctrl_dir);
2162: if((fp=fopen(aliasfile,"r"))==NULL)
2163: return(FALSE);
2164:
2165: SAFECOPY(alias,fullalias);
2166: p=strrchr(alias+1,'/');
2167: if(p) {
2168: *p=0;
2169: fname=p+1;
2170: }
2171:
2172: if(filename==NULL /* directory */ && *fname /* filename specified */) {
2173: fclose(fp);
2174: return(FALSE);
2175: }
2176:
2177: while(!feof(fp)) {
2178: if(!fgets(line,sizeof(line),fp))
2179: break;
2180:
2181: p=line; /* alias */
2182: while(*p && *p<=' ') p++;
2183: if(*p==';') /* comment */
2184: continue;
2185:
2186: tp=p; /* terminator */
2187: while(*tp && *tp>' ') tp++;
2188: if(*tp) *tp=0;
2189:
2190: if(stricmp(p,alias)) /* Not a match */
2191: continue;
2192:
2193: p=tp+1; /* filename */
2194: while(*p && *p<=' ') p++;
2195:
2196: tp=p; /* terminator */
2197: while(*tp && *tp>' ') tp++;
2198: if(*tp) *tp=0;
2199:
2200: if(!strnicmp(p,BBS_VIRTUAL_PATH,strlen(BBS_VIRTUAL_PATH))) {
2201: if((dir=getdir(p+strlen(BBS_VIRTUAL_PATH),user))<0) {
2202: lprintf(LOG_WARNING,"0000 !Invalid virtual path (%s) for %s",p,user->alias);
2203: /* invalid or no access */
2204: continue;
2205: }
2206: p=strrchr(p,'/');
2207: if(p!=NULL) p++;
2208: if(p!=NULL && filename!=NULL) {
2209: if(*p)
2210: sprintf(filename,"%s%s",scfg.dir[dir]->path,p);
2211: else
2212: sprintf(filename,"%s%s",scfg.dir[dir]->path,fname);
2213: }
2214: } else if(filename!=NULL)
2215: strcpy(filename,p);
2216:
2217: result=TRUE; /* success */
2218: break;
2219: }
2220: fclose(fp);
2221: if(curdir!=NULL)
2222: *curdir=dir;
2223: return(result);
2224: }
2225:
2226: char* root_dir(char* path)
2227: {
2228: char* p;
2229: static char root[MAX_PATH+1];
2230:
2231: SAFECOPY(root,path);
2232:
2233: if(!strncmp(root,"\\\\",2)) { /* network path */
2234: p=strchr(root+2,'\\');
2235: if(p) p=strchr(p+1,'\\');
2236: if(p) *(p+1)=0; /* truncate at \\computer\sharename\ */
2237: }
2238: else if(!strncmp(root+1,":/",2) || !strncmp(root+1,":\\",2))
2239: root[3]=0;
2240: else if(*root=='/' || *root=='\\')
2241: root[1]=0;
2242:
2243: return(root);
2244: }
2245:
2246: char* vpath(int lib, int dir, char* str)
2247: {
2248: strcpy(str,"/");
2249: if(lib<0)
2250: return(str);
2251: strcat(str,scfg.lib[lib]->sname);
2252: strcat(str,"/");
2253: if(dir<0)
2254: return(str);
2255: strcat(str,scfg.dir[dir]->code_suffix);
2256: strcat(str,"/");
2257: return(str);
2258: }
2259:
2260: void ftp_printfile(SOCKET sock, const char* name, unsigned code)
2261: {
2262: char path[MAX_PATH+1];
2263: char buf[512];
2264: FILE* fp;
2265: unsigned i;
2266:
2267: SAFEPRINTF2(path,"%sftp%s.txt",scfg.text_dir,name);
2268: if((fp=fopen(path,"rb"))!=NULL) {
2269: i=0;
2270: while(!feof(fp)) {
2271: if(!fgets(buf,sizeof(buf),fp))
2272: break;
2273: truncsp(buf);
2274: if(!i)
2275: sockprintf(sock,"%u-%s",code,buf);
2276: else
2277: sockprintf(sock," %s",buf);
2278: i++;
2279: }
2280: fclose(fp);
2281: }
2282: }
2283:
2284: static BOOL badlogin(SOCKET sock, ulong* login_attempts)
2285: {
2286: mswait(5000); /* As recommended by RFC2577 */
2287: if(++(*login_attempts)>=3) {
2288: sockprintf(sock,"421 Too many failed login attempts.");
2289: return(TRUE);
2290: }
2291: ftp_printfile(sock,"badlogin",530);
2292: sockprintf(sock,"530 Invalid login.");
2293: return(FALSE);
2294: }
2295:
2296: static char* ftp_tmpfname(char* str, SOCKET sock)
2297: {
2298: safe_snprintf(str,MAX_PATH,"%sSBBS_FTP.%u.%u.tx",scfg.temp_dir,getpid(),sock);
2299: return(str);
2300: }
2301:
2302: static void ctrl_thread(void* arg)
2303: {
2304: char buf[512];
2305: char str[128];
2306: char* cmd;
2307: char* p;
2308: char* np;
2309: char* tp;
2310: char* dp;
2311: char* mode="active";
2312: char password[64];
2313: char fname[MAX_PATH+1];
2314: char qwkfile[MAX_PATH+1];
2315: char aliasfile[MAX_PATH+1];
2316: char aliasline[512];
2317: char desc[501]="";
2318: char sys_pass[128];
2319: char* host_name;
2320: char host_ip[64];
2321: char path[MAX_PATH+1];
2322: char local_dir[MAX_PATH+1];
2323: char ren_from[MAX_PATH+1]="";
2324: char html_index_ext[MAX_PATH+1];
2325: WORD port;
2326: ulong ip_addr;
2327: socklen_t addr_len;
2328: DWORD h1,h2,h3,h4;
2329: u_short p1,p2; /* For PORT command */
2330: int i;
2331: int rd;
2332: int result;
2333: int lib;
2334: int dir;
2335: int curlib=-1;
2336: int curdir=-1;
2337: int orglib;
2338: int orgdir;
2339: long filepos=0L;
2340: long timeleft;
2341: ulong l;
2342: ulong login_attempts=0;
2343: ulong avail; /* disk space */
2344: ulong count;
2345: BOOL detail;
2346: BOOL success;
2347: BOOL getdate;
2348: BOOL getsize;
2349: BOOL delecmd;
2350: BOOL delfile;
2351: BOOL tmpfile;
2352: BOOL credits;
2353: BOOL filedat=FALSE;
2354: BOOL transfer_inprogress;
2355: BOOL transfer_aborted;
2356: BOOL sysop=FALSE;
2357: BOOL local_fsys=FALSE;
2358: BOOL alias_dir;
2359: BOOL append;
2360: BOOL reuseaddr;
2361: FILE* fp;
2362: FILE* alias_fp;
2363: SOCKET sock;
2364: SOCKET tmp_sock;
2365: SOCKET pasv_sock=INVALID_SOCKET;
2366: SOCKET data_sock=INVALID_SOCKET;
2367: HOSTENT* host;
2368: SOCKADDR_IN addr;
2369: SOCKADDR_IN data_addr;
2370: SOCKADDR_IN pasv_addr;
2371: ftp_t ftp=*(ftp_t*)arg;
2372: user_t user;
2373: time_t t;
2374: time_t now;
2375: time_t logintime=0;
2376: time_t lastactive;
2377: file_t f;
2378: glob_t g;
2379: node_t node;
2380: client_t client;
2381: struct tm tm;
2382: struct tm cur_tm;
2383: #ifdef JAVASCRIPT
2384: jsval js_val;
2385: JSRuntime* js_runtime=NULL;
2386: JSContext* js_cx=NULL;
2387: JSObject* js_glob;
2388: JSObject* js_ftp;
2389: JSString* js_str;
2390: #endif
2391:
2392: thread_up(TRUE /* setuid */);
2393:
2394: lastactive=time(NULL);
2395:
2396: sock=ftp.socket;
2397: data_addr=ftp.client_addr;
2398: /* Default data port is ctrl port-1 */
2399: data_addr.sin_port=ntohs(data_addr.sin_port)-1;
2400: data_addr.sin_port=htons(data_addr.sin_port);
2401:
2402: lprintf(LOG_DEBUG,"%04d CTRL thread started", sock);
2403:
2404: free(arg); /* unexplicable assertion here on July 26, 2001 */
2405:
2406: #ifdef _WIN32
2407: if(startup->answer_sound[0] && !(startup->options&FTP_OPT_MUTE))
2408: PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);
2409: #endif
2410:
2411: transfer_inprogress = FALSE;
2412: transfer_aborted = FALSE;
2413:
2414: l=1;
2415:
2416: if((i=ioctlsocket(sock, FIONBIO, &l))!=0) {
2417: lprintf(LOG_ERR,"%04d !ERROR %d (%d) disabling socket blocking"
2418: ,sock, i, ERROR_VALUE);
2419: sockprintf(sock,"425 Error %d disabling socket blocking"
2420: ,ERROR_VALUE);
2421: ftp_close_socket(&sock,__LINE__);
2422: thread_down();
2423: return;
2424: }
2425:
2426: memset(&user,0,sizeof(user));
2427:
2428: SAFECOPY(host_ip,inet_ntoa(ftp.client_addr.sin_addr));
2429:
2430: lprintf(LOG_DEBUG,"%04d CTRL connection accepted from: %s port %u"
2431: ,sock, host_ip, ntohs(ftp.client_addr.sin_port));
2432:
2433: if(startup->options&FTP_OPT_NO_HOST_LOOKUP)
2434: host=NULL;
2435: else
2436: host=gethostbyaddr ((char *)&ftp.client_addr.sin_addr
2437: ,sizeof(ftp.client_addr.sin_addr),AF_INET);
2438:
2439: if(host!=NULL && host->h_name!=NULL)
2440: host_name=host->h_name;
2441: else
2442: host_name="<no name>";
2443:
2444: if(!(startup->options&FTP_OPT_NO_HOST_LOOKUP)) {
2445: lprintf(LOG_INFO,"%04d Hostname: %s", sock, host_name);
2446: for(i=0;host!=NULL && host->h_aliases!=NULL && host->h_aliases[i]!=NULL;i++)
2447: lprintf(LOG_INFO,"%04d HostAlias: %s", sock, host->h_aliases[i]);
2448: }
2449:
2450: if(trashcan(&scfg,host_ip,"ip")) {
2451: lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s", sock, host_ip);
2452: sockprintf(sock,"550 Access denied.");
2453: ftp_close_socket(&sock,__LINE__);
2454: thread_down();
2455: return;
2456: }
2457:
2458: if(trashcan(&scfg,host_name,"host")) {
2459: lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in host.can: %s", sock, host_name);
2460: sockprintf(sock,"550 Access denied.");
2461: ftp_close_socket(&sock,__LINE__);
2462: thread_down();
2463: return;
2464: }
2465:
2466: /* For PASV mode */
2467: addr_len=sizeof(pasv_addr);
2468: if((result=getsockname(sock, (struct sockaddr *)&pasv_addr,&addr_len))!=0) {
2469: lprintf(LOG_ERR,"%04d !ERROR %d (%d) getting address/port", sock, result, ERROR_VALUE);
2470: sockprintf(sock,"425 Error %d getting address/port",ERROR_VALUE);
2471: ftp_close_socket(&sock,__LINE__);
2472: thread_down();
2473: return;
2474: }
2475:
2476: active_clients++, update_clients();
2477:
2478: /* Initialize client display */
2479: client.size=sizeof(client);
2480: client.time=time(NULL);
2481: SAFECOPY(client.addr,host_ip);
2482: SAFECOPY(client.host,host_name);
2483: client.port=ntohs(ftp.client_addr.sin_port);
2484: client.protocol="FTP";
2485: client.user="<unknown>";
2486: client_on(sock,&client,FALSE /* update */);
2487:
2488: sockprintf(sock,"220-%s (%s)",scfg.sys_name, startup->host_name);
2489: sockprintf(sock," Synchronet FTP Server %s-%s Ready"
2490: ,revision,PLATFORM_DESC);
2491: sprintf(str,"%sftplogin.txt",scfg.text_dir);
2492: if((fp=fopen(str,"rb"))!=NULL) {
2493: while(!feof(fp)) {
2494: if(!fgets(buf,sizeof(buf),fp))
2495: break;
2496: truncsp(buf);
2497: sockprintf(sock," %s",buf);
2498: }
2499: fclose(fp);
2500: }
2501: sockprintf(sock,"220 Please enter your user name.");
2502:
2503: #ifdef SOCKET_DEBUG_CTRL
2504: socket_debug[sock]|=SOCKET_DEBUG_CTRL;
2505: #endif
2506: while(1) {
2507:
2508: #ifdef SOCKET_DEBUG_READLINE
2509: socket_debug[sock]|=SOCKET_DEBUG_READLINE;
2510: #endif
2511: rd = sockreadline(sock, buf, sizeof(buf), &lastactive);
2512: #ifdef SOCKET_DEBUG_READLINE
2513: socket_debug[sock]&=~SOCKET_DEBUG_READLINE;
2514: #endif
2515: if(rd<1) {
2516: if(transfer_inprogress==TRUE) {
2517: lprintf(LOG_WARNING,"%04d Aborting transfer due to receive error",sock);
2518: transfer_aborted=TRUE;
2519: }
2520: break;
2521: }
2522: truncsp(buf);
2523: lastactive=time(NULL);
2524: cmd=buf;
2525: while(((BYTE)*cmd)==TELNET_IAC) {
2526: cmd++;
2527: lprintf(LOG_DEBUG,"%04d RX: Telnet cmd: %s",sock,telnet_cmd_desc(*cmd));
2528: cmd++;
2529: }
2530: while(*cmd && *cmd<' ') {
2531: lprintf(LOG_DEBUG,"%04d RX: %d (0x%02X)",sock,(BYTE)*cmd,(BYTE)*cmd);
2532: cmd++;
2533: }
2534: if(!(*cmd))
2535: continue;
2536: if(startup->options&FTP_OPT_DEBUG_RX)
2537: lprintf(LOG_DEBUG,"%04d RX: %s", sock, cmd);
2538: if(!stricmp(cmd, "NOOP")) {
2539: sockprintf(sock,"200 NOOP command successful.");
2540: continue;
2541: }
2542: if(!stricmp(cmd, "HELP SITE") || !stricmp(cmd, "SITE HELP")) {
2543: sockprintf(sock,"214-The following SITE commands are recognized (* => unimplemented):");
2544: sockprintf(sock," HELP VER WHO UPTIME");
2545: if(user.level>=SYSOP_LEVEL)
2546: sockprintf(sock,
2547: " RECYCLE [ALL]");
2548: if(sysop)
2549: sockprintf(sock,
2550: " EXEC <cmd>");
2551: sockprintf(sock,"214 Direct comments to sysop@%s.",scfg.sys_inetaddr);
2552: continue;
2553: }
2554: if(!strnicmp(cmd, "HELP",4)) {
2555: sockprintf(sock,"214-The following commands are recognized (* => unimplemented, # => extension):");
2556: sockprintf(sock," USER PASS CWD XCWD CDUP XCUP PWD XPWD");
2557: sockprintf(sock," QUIT REIN PORT PASV LIST NLST NOOP HELP");
2558: sockprintf(sock," SIZE MDTM RETR STOR REST ALLO ABOR SYST");
2559: sockprintf(sock," TYPE STRU MODE SITE RNFR* RNTO* DELE* DESC#");
2560: sockprintf(sock," FEAT# OPTS#");
2561: sockprintf(sock,"214 Direct comments to sysop@%s.",scfg.sys_inetaddr);
2562: continue;
2563: }
2564: if(!stricmp(cmd, "FEAT")) {
2565: sockprintf(sock,"211-The following additional (post-RFC949) features are supported:");
2566: sockprintf(sock," DESC");
2567: sockprintf(sock," MDTM");
2568: sockprintf(sock," SIZE");
2569: sockprintf(sock," REST STREAM");
2570: sockprintf(sock,"211 End");
2571: continue;
2572: }
2573: if(!strnicmp(cmd, "OPTS",4)) {
2574: sockprintf(sock,"501 No options supported.");
2575: continue;
2576: }
2577: if(!stricmp(cmd, "QUIT")) {
2578: ftp_printfile(sock,"bye",221);
2579: sockprintf(sock,"221 Goodbye. Closing control connection.");
2580: break;
2581: }
2582: if(!strnicmp(cmd, "USER ",5)) {
2583: sysop=FALSE;
2584: user.number=0;
2585: p=cmd+5;
2586: while(*p && *p<=' ') p++;
2587: truncsp(p);
2588: SAFECOPY(user.alias,p);
2589: user.number=matchuser(&scfg,user.alias,FALSE /*sysop_alias*/);
2590: if(!user.number && !stricmp(user.alias,"anonymous"))
2591: user.number=matchuser(&scfg,"guest",FALSE);
2592: if(user.number && getuserdat(&scfg, &user)==0 && user.pass[0]==0)
2593: sockprintf(sock,"331 User name okay, give your full e-mail address as password.");
2594: else
2595: sockprintf(sock,"331 User name okay, need password.");
2596: user.number=0;
2597: continue;
2598: }
2599: if(!strnicmp(cmd, "PASS ",5) && user.alias[0]) {
2600: user.number=0;
2601: p=cmd+5;
2602: while(*p && *p<=' ') p++;
2603:
2604: SAFECOPY(password,p);
2605: user.number=matchuser(&scfg,user.alias,FALSE /*sysop_alias*/);
2606: if(!user.number) {
2607: if(scfg.sys_misc&SM_ECHO_PW)
2608: lprintf(LOG_WARNING,"%04d !UNKNOWN USER: %s, Password: %s",sock,user.alias,p);
2609: else
2610: lprintf(LOG_WARNING,"%04d !UNKNOWN USER: %s",sock,user.alias);
2611: if(badlogin(sock,&login_attempts))
2612: break;
2613: continue;
2614: }
2615: if((i=getuserdat(&scfg, &user))!=0) {
2616: lprintf(LOG_ERR,"%04d !ERROR %d getting data for user #%d (%s)"
2617: ,sock,i,user.number,user.alias);
2618: sockprintf(sock,"530 Database error %d",i);
2619: user.number=0;
2620: continue;
2621: }
2622: if(user.misc&(DELETED|INACTIVE)) {
2623: lprintf(LOG_WARNING,"%04d !DELETED or INACTIVE user #%d (%s)"
2624: ,sock,user.number,user.alias);
2625: user.number=0;
2626: if(badlogin(sock,&login_attempts))
2627: break;
2628: continue;
2629: }
2630: if(user.rest&FLAG('T')) {
2631: lprintf(LOG_WARNING,"%04d !T RESTRICTED user #%d (%s)"
2632: ,sock,user.number,user.alias);
2633: user.number=0;
2634: if(badlogin(sock,&login_attempts))
2635: break;
2636: continue;
2637: }
2638: if(user.ltoday>scfg.level_callsperday[user.level]
2639: && !(user.exempt&FLAG('L'))) {
2640: lprintf(LOG_WARNING,"%04d !MAXIMUM LOGONS (%d) reached for %s"
2641: ,sock,scfg.level_callsperday[user.level],user.alias);
2642: sockprintf(sock,"530 Maximum logons reached.");
2643: user.number=0;
2644: continue;
2645: }
2646: if(user.rest&FLAG('L') && user.ltoday>1) {
2647: lprintf(LOG_WARNING,"%04d !L RESTRICTED user #%d (%s) already on today"
2648: ,sock,user.number,user.alias);
2649: sockprintf(sock,"530 Maximum logons reached.");
2650: user.number=0;
2651: continue;
2652: }
2653:
2654: SAFEPRINTF2(sys_pass,"%s:%s",user.pass,scfg.sys_pass);
2655: if(!user.pass[0]) { /* Guest/Anonymous */
2656: if(trashcan(&scfg,password,"email")) {
2657: lprintf(LOG_NOTICE,"%04d !BLOCKED e-mail address: %s",sock,password);
2658: user.number=0;
2659: if(badlogin(sock,&login_attempts))
2660: break;
2661: continue;
2662: }
2663: lprintf(LOG_INFO,"%04d %s: <%s>",sock,user.alias,password);
2664: putuserrec(&scfg,user.number,U_NETMAIL,LEN_NETMAIL,password);
2665: }
2666: else if(user.level>=SYSOP_LEVEL && !stricmp(password,sys_pass)) {
2667: lprintf(LOG_INFO,"%04d Sysop access granted to %s", sock, user.alias);
2668: sysop=TRUE;
2669: }
2670: else if(stricmp(password,user.pass)) {
2671: if(scfg.sys_misc&SM_ECHO_PW)
2672: lprintf(LOG_WARNING,"%04d !FAILED Password attempt for user %s: '%s' expected '%s'"
2673: ,sock, user.alias, password, user.pass);
2674: else
2675: lprintf(LOG_WARNING,"%04d !FAILED Password attempt for user %s"
2676: ,sock, user.alias);
2677: user.number=0;
2678: if(badlogin(sock,&login_attempts))
2679: break;
2680: continue;
2681: }
2682:
2683: /* Update client display */
2684: if(user.pass[0])
2685: client.user=user.alias;
2686: else { /* anonymous */
2687: sprintf(str,"%s <%.32s>",user.alias,password);
2688: client.user=str;
2689: }
2690: client_on(sock,&client,TRUE /* update */);
2691:
2692:
2693: lprintf(LOG_INFO,"%04d %s logged in",sock,user.alias);
2694: logintime=time(NULL);
2695: timeleft=gettimeleft(&scfg,&user,logintime);
2696: ftp_printfile(sock,"hello",230);
2697:
2698: #ifdef JAVASCRIPT
2699: #ifdef JS_CX_PER_SESSION
2700: if(js_cx!=NULL) {
2701:
2702: if(js_CreateUserClass(js_cx, js_glob, &scfg)==NULL)
2703: lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user class",sock);
2704:
2705: if(js_CreateUserObject(js_cx, js_glob, &scfg, "user", user.number)==NULL)
2706: lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user object",sock);
2707:
2708: if(js_CreateClientObject(js_cx, js_glob, "client", &client, sock)==NULL)
2709: lprintf(LOG_ERR,"%04d !JavaScript ERROR creating client object",sock);
2710:
2711: if(js_CreateFileAreaObject(js_cx, js_glob, &scfg, &user
2712: ,startup->html_index_file)==NULL)
2713: lprintf(LOG_ERR,"%04d !JavaScript ERROR creating file area object",sock);
2714:
2715: }
2716: #endif
2717: #endif
2718:
2719: if(sysop)
2720: sockprintf(sock,"230-Sysop access granted.");
2721: sockprintf(sock,"230-%s logged in.",user.alias);
2722: if(!(user.exempt&FLAG('D')) && (user.cdt+user.freecdt)>0)
2723: sockprintf(sock,"230-You have %lu download credits."
2724: ,user.cdt+user.freecdt);
2725: sockprintf(sock,"230 You are allowed %lu minutes of use for this session."
2726: ,timeleft/60);
2727: sprintf(qwkfile,"%sfile/%04d.qwk",scfg.data_dir,user.number);
2728:
2729: /* Adjust User Total Logons/Logons Today */
2730: adjustuserrec(&scfg,user.number,U_LOGONS,5,1);
2731: putuserrec(&scfg,user.number,U_LTODAY,5,ultoa(user.ltoday+1,str,10));
2732: putuserrec(&scfg,user.number,U_MODEM,LEN_MODEM,"FTP");
2733: putuserrec(&scfg,user.number,U_COMP,LEN_COMP,host_name);
2734: putuserrec(&scfg,user.number,U_NOTE,LEN_NOTE,host_ip);
2735: putuserrec(&scfg,user.number,U_LOGONTIME,0,ultoa(logintime,str,16));
2736: getuserdat(&scfg, &user); /* make user current */
2737:
2738: continue;
2739: }
2740:
2741: if(!user.number) {
2742: sockprintf(sock,"530 Please login with USER and PASS.");
2743: continue;
2744: }
2745:
2746: if(!(user.rest&FLAG('G')))
2747: getuserdat(&scfg, &user); /* get current user data */
2748:
2749: if((timeleft=gettimeleft(&scfg,&user,logintime))<1L) {
2750: sockprintf(sock,"421 Sorry, you've run out of time.");
2751: lprintf(LOG_WARNING,"%04d Out of time, disconnecting",sock);
2752: break;
2753: }
2754:
2755: /********************************/
2756: /* These commands require login */
2757: /********************************/
2758:
2759: if(!stricmp(cmd, "REIN")) {
2760: lprintf(LOG_INFO,"%04d %s reinitialized control session",sock,user.alias);
2761: user.number=0;
2762: sysop=FALSE;
2763: filepos=0;
2764: sockprintf(sock,"220 Control session re-initialized. Ready for re-login.");
2765: continue;
2766: }
2767:
2768: if(!stricmp(cmd, "SITE WHO")) {
2769: sockprintf(sock,"211-Active Telnet Nodes:");
2770: for(i=0;i<scfg.sys_nodes && i<scfg.sys_lastnode;i++) {
2771: if((result=getnodedat(&scfg, i+1, &node, 0))!=0) {
2772: sockprintf(sock," Error %d getting data for Telnet Node %d",result,i+1);
2773: continue;
2774: }
2775: if(node.status==NODE_INUSE)
2776: sockprintf(sock," Node %3d: %s",i+1, username(&scfg,node.useron,str));
2777: }
2778: sockprintf(sock,"211 End (%d active FTP clients)", active_clients);
2779: continue;
2780: }
2781: if(!stricmp(cmd, "SITE VER")) {
2782: sockprintf(sock,"211 %s",ftp_ver());
2783: continue;
2784: }
2785: if(!stricmp(cmd, "SITE UPTIME")) {
2786: sockprintf(sock,"211 %s (%lu served)",sectostr(time(NULL)-uptime,str),served);
2787: continue;
2788: }
2789: if(!stricmp(cmd, "SITE RECYCLE") && user.level>=SYSOP_LEVEL) {
2790: startup->recycle_now=TRUE;
2791: sockprintf(sock,"211 server will recycle when not in-use");
2792: continue;
2793: }
2794: if(!stricmp(cmd, "SITE RECYCLE ALL") && user.level>=SYSOP_LEVEL) {
2795: refresh_cfg(&scfg);
2796: sockprintf(sock,"211 ALL servers/nodes will recycle when not in-use");
2797: continue;
2798: }
2799: if(!strnicmp(cmd,"SITE EXEC ",10) && sysop) {
2800: p=cmd+10;
2801: while(*p && *p<=' ') p++;
2802: #ifdef __unix__
2803: fp=popen(p,"r");
2804: if(fp==NULL)
2805: sockprintf(sock,"500 Error %d opening pipe to: %s",errno,p);
2806: else {
2807: while(!feof(fp)) {
2808: if(fgets(str,sizeof(str),fp)==NULL)
2809: break;
2810: sockprintf(sock,"200-%s",str);
2811: }
2812: sockprintf(sock,"200 %s returned %d",p,pclose(fp));
2813: }
2814: #else
2815: sockprintf(sock,"200 system(%s) returned %d",p,system(p));
2816: #endif
2817: continue;
2818: }
2819:
2820:
2821: #ifdef SOCKET_DEBUG_CTRL
2822: if(!stricmp(cmd, "SITE DEBUG")) {
2823: sockprintf(sock,"211-Debug");
2824: for(i=0;i<sizeof(socket_debug);i++)
2825: if(socket_debug[i]!=0)
2826: sockprintf(sock,"211-socket %d = 0x%X",i,socket_debug[i]);
2827: sockprintf(sock,"211 End");
2828: continue;
2829: }
2830: #endif
2831:
2832: if(!strnicmp(cmd, "PORT ",5)) {
2833:
2834: if(pasv_sock!=INVALID_SOCKET)
2835: ftp_close_socket(&pasv_sock,__LINE__);
2836:
2837: p=cmd+5;
2838: while(*p && *p<=' ') p++;
2839: sscanf(p,"%ld,%ld,%ld,%ld,%hd,%hd",&h1,&h2,&h3,&h4,&p1,&p2);
2840: data_addr.sin_addr.s_addr=htonl((h1<<24)|(h2<<16)|(h3<<8)|h4);
2841: data_addr.sin_port=(u_short)((p1<<8)|p2);
2842: if(data_addr.sin_port< IPPORT_RESERVED) {
2843: lprintf(LOG_WARNING,"%04d !SUSPECTED BOUNCE ATTACK ATTEMPT by %s to %s port %u"
2844: ,sock,user.alias
2845: ,inet_ntoa(data_addr.sin_addr),data_addr.sin_port);
2846: hacklog(&scfg, "FTP", user.alias, cmd, host_name, &ftp.client_addr);
2847: sockprintf(sock,"504 Bad port number.");
2848: #ifdef _WIN32
2849: if(startup->hack_sound[0] && !(startup->options&FTP_OPT_MUTE))
2850: PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME);
2851: #endif
2852: continue; /* As recommended by RFC2577 */
2853: }
2854: data_addr.sin_port=htons(data_addr.sin_port);
2855: sockprintf(sock,"200 PORT Command successful.");
2856: mode="active";
2857: continue;
2858: }
2859:
2860: if(!stricmp(cmd, "PASV")
2861: || !stricmp(cmd, "P@SW")) { /* Kludge required for SMC Barricade V1.2 */
2862:
2863: if(pasv_sock!=INVALID_SOCKET)
2864: ftp_close_socket(&pasv_sock,__LINE__);
2865:
2866: if((pasv_sock=ftp_open_socket(SOCK_STREAM))==INVALID_SOCKET) {
2867: lprintf(LOG_WARNING,"%04d !PASV ERROR %d opening socket", sock,ERROR_VALUE);
2868: sockprintf(sock,"425 Error %d opening PASV data socket", ERROR_VALUE);
2869: continue;
2870: }
2871:
2872: reuseaddr=FALSE;
2873: if((result=setsockopt(pasv_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&reuseaddr,sizeof(reuseaddr)))!=0) {
2874: lprintf(LOG_WARNING,"%04d !PASV ERROR %d disabling REUSEADDR socket option"
2875: ,sock,ERROR_VALUE);
2876: sockprintf(sock,"425 Error %d disabling REUSEADDR socket option", ERROR_VALUE);
2877: continue;
2878: }
2879:
2880: if(startup->options&FTP_OPT_DEBUG_DATA)
2881: lprintf(LOG_DEBUG,"%04d PASV DATA socket %d opened",sock,pasv_sock);
2882:
2883: for(port=startup->pasv_port_low; port<=startup->pasv_port_high; port++) {
2884:
2885: if(startup->options&FTP_OPT_DEBUG_DATA)
2886: lprintf(LOG_DEBUG,"%04d PASV DATA trying to bind socket to port %u"
2887: ,sock,port);
2888:
2889: pasv_addr.sin_port = htons(port);
2890:
2891: if((result=bind(pasv_sock, (struct sockaddr *) &pasv_addr,sizeof(pasv_addr)))==0)
2892: break;
2893: }
2894: if(result!= 0) {
2895: lprintf(LOG_ERR,"%04d !PASV ERROR %d (%d) binding socket to port %u"
2896: ,sock, result, ERROR_VALUE, port);
2897: sockprintf(sock,"425 Error %d binding data socket",ERROR_VALUE);
2898: ftp_close_socket(&pasv_sock,__LINE__);
2899: continue;
2900: }
2901: if(startup->options&FTP_OPT_DEBUG_DATA)
2902: lprintf(LOG_DEBUG,"%04d PASV DATA socket %d bound to port %u",sock,pasv_sock,port);
2903:
2904: addr_len=sizeof(addr);
2905: if((result=getsockname(pasv_sock, (struct sockaddr *)&addr,&addr_len))!=0) {
2906: lprintf(LOG_ERR,"%04d !PASV ERROR %d (%d) getting address/port"
2907: ,sock, result, ERROR_VALUE);
2908: sockprintf(sock,"425 Error %d getting address/port",ERROR_VALUE);
2909: ftp_close_socket(&pasv_sock,__LINE__);
2910: continue;
2911: }
2912:
2913: if((result=listen(pasv_sock, 1))!= 0) {
2914: lprintf(LOG_ERR,"%04d !PASV ERROR %d (%d) listening on port %u"
2915: ,sock, result, ERROR_VALUE,port);
2916: sockprintf(sock,"425 Error %d listening on data socket",ERROR_VALUE);
2917: ftp_close_socket(&pasv_sock,__LINE__);
2918: continue;
2919: }
2920:
2921: /* Choose IP address to use in passive response */
2922: ip_addr=0;
2923: if(startup->options&FTP_OPT_LOOKUP_PASV_IP
2924: && (host=gethostbyname(startup->host_name))!=NULL)
2925: ip_addr=ntohl(*((ulong*)host->h_addr_list[0]));
2926: if(ip_addr==0 && (ip_addr=startup->pasv_ip_addr)==0)
2927: ip_addr=ntohl(pasv_addr.sin_addr.s_addr);
2928:
2929: if(startup->options&FTP_OPT_DEBUG_DATA)
2930: lprintf(LOG_INFO,"%04d PASV DATA IP address in response: %u.%u.%u.%u (subject to NAT)"
2931: ,sock
2932: ,(ip_addr>>24)&0xff
2933: ,(ip_addr>>16)&0xff
2934: ,(ip_addr>>8)&0xff
2935: ,ip_addr&0xff
2936: );
2937: port=ntohs(addr.sin_port);
2938: sockprintf(sock,"227 Entering Passive Mode (%u,%u,%u,%u,%hu,%hu)"
2939: ,(ip_addr>>24)&0xff
2940: ,(ip_addr>>16)&0xff
2941: ,(ip_addr>>8)&0xff
2942: ,ip_addr&0xff
2943: ,(port>>8)&0xff
2944: ,port&0xff
2945: );
2946: mode="passive";
2947: continue;
2948: }
2949:
2950: if(!strnicmp(cmd, "TYPE ",5)) {
2951: sockprintf(sock,"200 All files sent in BINARY mode.");
2952: continue;
2953: }
2954:
2955: if(!strnicmp(cmd, "ALLO",4)) {
2956: p=cmd+5;
2957: while(*p && *p<=' ') p++;
2958: if(*p)
2959: l=atol(p);
2960: else
2961: l=0;
2962: if(local_fsys)
2963: avail=getfreediskspace(local_dir,0);
2964: else
2965: avail=getfreediskspace(scfg.data_dir,0); /* Change to temp_dir? */
2966: if(l && l>avail)
2967: sockprintf(sock,"504 Only %lu bytes available.",avail);
2968: else
2969: sockprintf(sock,"200 %lu bytes available.",avail);
2970: continue;
2971: }
2972:
2973: if(!strnicmp(cmd, "REST",4)) {
2974: p=cmd+4;
2975: while(*p && *p<=' ') p++;
2976: if(*p)
2977: filepos=atol(p);
2978: else
2979: filepos=0;
2980: sockprintf(sock,"350 Restarting at %lu. Send STORE or RETRIEVE to initiate transfer."
2981: ,filepos);
2982: continue;
2983: }
2984:
2985: if(!strnicmp(cmd, "MODE ",5)) {
2986: p=cmd+5;
2987: while(*p && *p<=' ') p++;
2988: if(toupper(*p)!='S')
2989: sockprintf(sock,"504 Only STREAM mode supported.");
2990: else
2991: sockprintf(sock,"200 STREAM mode.");
2992: continue;
2993: }
2994:
2995: if(!strnicmp(cmd, "STRU ",5)) {
2996: p=cmd+5;
2997: while(*p && *p<=' ') p++;
2998: if(toupper(*p)!='F')
2999: sockprintf(sock,"504 Only FILE structure supported.");
3000: else
3001: sockprintf(sock,"200 FILE structure.");
3002: continue;
3003: }
3004:
3005: if(!stricmp(cmd, "SYST")) {
3006: sockprintf(sock,"215 UNIX Type: L8");
3007: continue;
3008: }
3009:
3010: if(!stricmp(cmd, "ABOR")) {
3011: if(!transfer_inprogress)
3012: sockprintf(sock,"226 No tranfer in progress.");
3013: else {
3014: lprintf(LOG_WARNING,"%04d %s aborting transfer"
3015: ,sock,user.alias);
3016: transfer_aborted=TRUE;
3017: YIELD(); /* give send thread time to abort */
3018: sockprintf(sock,"226 Transfer aborted.");
3019: }
3020: continue;
3021: }
3022:
3023: if(!strnicmp(cmd,"SMNT ",5) && sysop && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)) {
3024: p=cmd+5;
3025: while(*p && *p<=' ') p++;
3026: if(!stricmp(p,BBS_FSYS_DIR))
3027: local_fsys=FALSE;
3028: else {
3029: if(!direxist(p)) {
3030: sockprintf(sock,"550 Directory does not exist.");
3031: lprintf(LOG_WARNING,"%04d !%s attempted to mount invalid directory: %s"
3032: ,sock, user.alias, p);
3033: continue;
3034: }
3035: local_fsys=TRUE;
3036: SAFECOPY(local_dir,p);
3037: }
3038: sockprintf(sock,"250 %s file system mounted."
3039: ,local_fsys ? "Local" : "BBS");
3040: lprintf(LOG_INFO,"%04d %s mounted %s file system"
3041: ,sock, user.alias, local_fsys ? "local" : "BBS");
3042: continue;
3043: }
3044:
3045: /****************************/
3046: /* Local File System Access */
3047: /****************************/
3048: if(sysop && local_fsys && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)) {
3049: if(local_dir[0]
3050: && local_dir[strlen(local_dir)-1]!='\\'
3051: && local_dir[strlen(local_dir)-1]!='/')
3052: strcat(local_dir,"/");
3053:
3054: if(!strnicmp(cmd, "LIST", 4) || !strnicmp(cmd, "NLST", 4)) {
3055: if((fp=fopen(ftp_tmpfname(fname,sock),"w+b"))==NULL) {
3056: lprintf(LOG_ERR,"%04d !ERROR %d opening %s",sock,errno,fname);
3057: sockprintf(sock, "451 Insufficient system storage");
3058: continue;
3059: }
3060: if(!strnicmp(cmd, "LIST", 4))
3061: detail=TRUE;
3062: else
3063: detail=FALSE;
3064:
3065: p=cmd+4;
3066: while(*p && *p<=' ') p++;
3067:
3068: if(*p=='-') { /* -Letc */
3069: while(*p && *p>' ') p++;
3070: while(*p && *p<=' ') p++;
3071: }
3072:
3073: SAFEPRINTF2(path,"%s%s",local_dir, *p ? p : "*");
3074: lprintf(LOG_INFO,"%04d %s listing: %s in %s mode", sock, user.alias, path, mode);
3075: sockprintf(sock, "150 Directory of %s%s", local_dir, p);
3076:
3077: now=time(NULL);
3078: if(localtime_r(&now,&cur_tm)==NULL)
3079: memset(&cur_tm,0,sizeof(cur_tm));
3080:
3081: glob(path,0,NULL,&g);
3082: for(i=0;i<(int)g.gl_pathc;i++) {
3083: if(detail) {
3084: f.size=flength(g.gl_pathv[i]);
3085: t=fdate(g.gl_pathv[i]);
3086: if(localtime_r(&t,&tm)==NULL)
3087: memset(&tm,0,sizeof(tm));
3088: fprintf(fp,"%crw-r--r-- 1 %-8s local %9ld %s %2d "
3089: ,isdir(g.gl_pathv[i]) ? 'd':'-'
3090: ,scfg.sys_id
3091: ,f.size
3092: ,ftp_mon[tm.tm_mon],tm.tm_mday);
3093: if(tm.tm_year==cur_tm.tm_year)
3094: fprintf(fp,"%02d:%02d %s\r\n"
3095: ,tm.tm_hour,tm.tm_min
3096: ,getfname(g.gl_pathv[i]));
3097: else
3098: fprintf(fp,"%5d %s\r\n"
3099: ,1900+tm.tm_year
3100: ,getfname(g.gl_pathv[i]));
3101: } else
3102: fprintf(fp,"%s\r\n",getfname(g.gl_pathv[i]));
3103: }
3104: globfree(&g);
3105: fclose(fp);
3106: filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,0L
3107: ,&transfer_inprogress,&transfer_aborted
3108: ,TRUE /* delfile */
3109: ,TRUE /* tmpfile */
3110: ,&lastactive,&user,-1,FALSE,FALSE,FALSE,NULL);
3111: continue;
3112: } /* Local LIST/NLST */
3113:
3114: if(!strnicmp(cmd, "CWD ", 4) || !strnicmp(cmd,"XCWD ",5)) {
3115: if(!strnicmp(cmd,"CWD ",4))
3116: p=cmd+4;
3117: else
3118: p=cmd+5;
3119: while(*p && *p<=' ') p++;
3120: tp=p;
3121: if(*tp=='/' || *tp=='\\') /* /local: and /bbs: are valid */
3122: tp++;
3123: if(!strnicmp(tp,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR))) {
3124: local_fsys=FALSE;
3125: sockprintf(sock,"250 CWD command successful (BBS file system mounted).");
3126: lprintf(LOG_INFO,"%04d %s mounted BBS file system", sock, user.alias);
3127: continue;
3128: }
3129: if(!strnicmp(tp,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) {
3130: tp+=strlen(LOCAL_FSYS_DIR); /* already mounted */
3131: p=tp;
3132: }
3133:
3134: if(p[1]==':' || !strncmp(p,"\\\\",2))
3135: SAFECOPY(path,p);
3136: else if(*p=='/' || *p=='\\')
3137: SAFEPRINTF2(path,"%s%s",root_dir(local_dir),p);
3138: else {
3139: SAFEPRINTF2(fname,"%s%s",local_dir,p);
3140: FULLPATH(path,fname,sizeof(path));
3141: }
3142:
3143: if(!direxist(path)) {
3144: sockprintf(sock,"550 Directory does not exist (%s).",path);
3145: lprintf(LOG_WARNING,"%04d !%s attempted to change to an invalid directory: %s"
3146: ,sock, user.alias, path);
3147: } else {
3148: SAFECOPY(local_dir,path);
3149: sockprintf(sock,"250 CWD command successful (%s).", local_dir);
3150: }
3151: continue;
3152: } /* Local CWD */
3153:
3154: if(!stricmp(cmd,"CDUP") || !stricmp(cmd,"XCUP")) {
3155: SAFEPRINTF(path,"%s..",local_dir);
3156: if(FULLPATH(local_dir,path,sizeof(local_dir))==NULL)
3157: sockprintf(sock,"550 Directory does not exist.");
3158: else
3159: sockprintf(sock,"200 CDUP command successful.");
3160: continue;
3161: }
3162:
3163: if(!stricmp(cmd, "PWD") || !stricmp(cmd,"XPWD")) {
3164: if(strlen(local_dir)>3)
3165: local_dir[strlen(local_dir)-1]=0; /* truncate '/' */
3166:
3167: sockprintf(sock,"257 \"%s\" is current directory."
3168: ,local_dir);
3169: continue;
3170: } /* Local PWD */
3171:
3172: if(!strnicmp(cmd, "MKD ", 4) || !strnicmp(cmd,"XMKD",4)) {
3173: p=cmd+4;
3174: while(*p && *p<=' ') p++;
3175: if(*p=='/') /* absolute */
3176: SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
3177: else /* relative */
3178: SAFEPRINTF2(fname,"%s%s",local_dir,p);
3179:
3180: if((i=MKDIR(fname))==0) {
3181: sockprintf(sock,"257 \"%s\" directory created",fname);
3182: lprintf(LOG_NOTICE,"%04d %s created directory: %s",sock,user.alias,fname);
3183: } else {
3184: sockprintf(sock,"521 Error %d creating directory: %s",i,fname);
3185: lprintf(LOG_WARNING,"%04d !%s attempted to create directory: %s (Error %d)"
3186: ,sock,user.alias,fname,i);
3187: }
3188: continue;
3189: }
3190:
3191: if(!strnicmp(cmd, "RMD ", 4) || !strnicmp(cmd,"XRMD",4)) {
3192: p=cmd+4;
3193: while(*p && *p<=' ') p++;
3194: if(*p=='/') /* absolute */
3195: SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
3196: else /* relative */
3197: SAFEPRINTF2(fname,"%s%s",local_dir,p);
3198:
3199: if((i=rmdir(fname))==0) {
3200: sockprintf(sock,"250 \"%s\" directory removed",fname);
3201: lprintf(LOG_NOTICE,"%04d %s removed directory: %s",sock,user.alias,fname);
3202: } else {
3203: sockprintf(sock,"450 Error %d removing directory: %s",i,fname);
3204: lprintf(LOG_WARNING,"%04d !%s attempted to remove directory: %s (Error %d)"
3205: ,sock,user.alias,fname,i);
3206: }
3207: continue;
3208: }
3209:
3210: if(!strnicmp(cmd, "RNFR ",5)) {
3211: p=cmd+5;
3212: while(*p && *p<=' ') p++;
3213: if(*p=='/') /* absolute */
3214: SAFEPRINTF2(ren_from,"%s%s",root_dir(local_dir),p+1);
3215: else /* relative */
3216: SAFEPRINTF2(ren_from,"%s%s",local_dir,p);
3217: if(!fexist(ren_from)) {
3218: sockprintf(sock,"550 File not found: %s",ren_from);
3219: lprintf(LOG_WARNING,"%04d !%s attempted to rename %s (not found)"
3220: ,sock,user.alias,ren_from);
3221: } else
3222: sockprintf(sock,"350 File exists, ready for destination name");
3223: continue;
3224: }
3225:
3226: if(!strnicmp(cmd, "RNTO ",5)) {
3227: p=cmd+5;
3228: while(*p && *p<=' ') p++;
3229: if(*p=='/') /* absolute */
3230: SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
3231: else /* relative */
3232: SAFEPRINTF2(fname,"%s%s",local_dir,p);
3233:
3234: if((i=rename(ren_from, fname))==0) {
3235: sockprintf(sock,"250 \"%s\" renamed to \"%s\"",ren_from,fname);
3236: lprintf(LOG_NOTICE,"%04d %s renamed %s to %s",sock,user.alias,ren_from,fname);
3237: } else {
3238: sockprintf(sock,"450 Error %d renaming file: %s",i,ren_from);
3239: lprintf(LOG_WARNING,"%04d !%s attempted to rename file: %s (Error %d)"
3240: ,sock,user.alias,ren_from,i);
3241: }
3242: continue;
3243: }
3244:
3245:
3246: if(!strnicmp(cmd, "RETR ", 5) || !strnicmp(cmd,"SIZE ",5)
3247: || !strnicmp(cmd, "MDTM ",5) || !strnicmp(cmd, "DELE ",5)) {
3248: p=cmd+5;
3249: while(*p && *p<=' ') p++;
3250:
3251: if(!strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR)))
3252: p+=strlen(LOCAL_FSYS_DIR); /* already mounted */
3253:
3254: if(p[1]==':') /* drive specified */
3255: SAFECOPY(fname,p);
3256: else if(*p=='/') /* absolute, current drive */
3257: SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
3258: else /* relative */
3259: SAFEPRINTF2(fname,"%s%s",local_dir,p);
3260: if(!fexist(fname)) {
3261: lprintf(LOG_WARNING,"%04d !%s file not found: %s",sock,user.alias,fname);
3262: sockprintf(sock,"550 File not found: %s",fname);
3263: continue;
3264: }
3265: if(!strnicmp(cmd,"SIZE ",5)) {
3266: sockprintf(sock,"213 %lu",flength(fname));
3267: continue;
3268: }
3269: if(!strnicmp(cmd,"MDTM ",5)) {
3270: t=fdate(fname);
3271: if(gmtime_r(&t,&tm)==NULL) /* specifically use GMT/UTC representation */
3272: memset(&tm,0,sizeof(tm));
3273: sockprintf(sock,"213 %u%02u%02u%02u%02u%02u"
3274: ,1900+tm.tm_year,tm.tm_mon+1,tm.tm_mday
3275: ,tm.tm_hour,tm.tm_min,tm.tm_sec);
3276: continue;
3277: }
3278: if(!strnicmp(cmd,"DELE ",5)) {
3279: if((i=remove(fname))==0) {
3280: sockprintf(sock,"250 \"%s\" removed successfully.",fname);
3281: lprintf(LOG_NOTICE,"%04d %s deleted file: %s",sock,user.alias,fname);
3282: } else {
3283: sockprintf(sock,"450 Error %d removing file: %s",i,fname);
3284: lprintf(LOG_WARNING,"%04d !%s attempted to delete file: %s (Error %d)"
3285: ,sock,user.alias,fname,i);
3286: }
3287: continue;
3288: }
3289: /* RETR */
3290: lprintf(LOG_INFO,"%04d %s downloading: %s (%lu bytes) in %s mode"
3291: ,sock,user.alias,fname,flength(fname)
3292: ,mode);
3293: sockprintf(sock,"150 Opening BINARY mode data connection for file transfer.");
3294: filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,filepos
3295: ,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
3296: ,&lastactive,&user,-1,FALSE,FALSE,FALSE,NULL);
3297: continue;
3298: } /* Local RETR/SIZE/MDTM */
3299:
3300: if(!strnicmp(cmd, "STOR ", 5) || !strnicmp(cmd, "APPE ", 5)) {
3301: p=cmd+5;
3302: while(*p && *p<=' ') p++;
3303:
3304: if(!strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR)))
3305: p+=strlen(LOCAL_FSYS_DIR); /* already mounted */
3306:
3307: if(p[1]==':') /* drive specified */
3308: SAFECOPY(fname,p);
3309: else if(*p=='/') /* absolute, current drive */
3310: SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
3311: else /* relative */
3312: SAFEPRINTF2(fname,"%s%s",local_dir,p);
3313:
3314: lprintf(LOG_INFO,"%04d %s uploading: %s in %s mode", sock,user.alias,fname
3315: ,mode);
3316: sockprintf(sock,"150 Opening BINARY mode data connection for file transfer.");
3317: filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,filepos
3318: ,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
3319: ,&lastactive
3320: ,&user
3321: ,-1 /* dir */
3322: ,TRUE /* uploading */
3323: ,FALSE /* credits */
3324: ,!strnicmp(cmd,"APPE",4) ? TRUE : FALSE /* append */
3325: ,NULL /* desc */
3326: );
3327: filepos=0;
3328: continue;
3329: } /* Local STOR */
3330: }
3331:
3332: if(!strnicmp(cmd, "LIST", 4) || !strnicmp(cmd, "NLST", 4)) {
3333: dir=curdir;
3334: lib=curlib;
3335:
3336: if(cmd[4]!=0)
3337: lprintf(LOG_DEBUG,"%04d LIST/NLST: %s",sock,cmd);
3338:
3339: /* path specified? */
3340: p=cmd+4;
3341: while(*p && *p<=' ') p++;
3342:
3343: if(*p=='-') { /* -Letc */
3344: while(*p && *p>' ') p++;
3345: while(*p && *p<=' ') p++;
3346: }
3347:
3348: parsepath(&p,&user,&lib,&dir);
3349:
3350: if((fp=fopen(ftp_tmpfname(fname,sock),"w+b"))==NULL) {
3351: lprintf(LOG_ERR,"%04d !ERROR %d opening %s",sock,errno,fname);
3352: sockprintf(sock, "451 Insufficient system storage");
3353: continue;
3354: }
3355: if(!strnicmp(cmd, "LIST", 4))
3356: detail=TRUE;
3357: else
3358: detail=FALSE;
3359: sockprintf(sock,"150 Opening ASCII mode data connection for /bin/ls.");
3360: now=time(NULL);
3361: if(localtime_r(&now,&cur_tm)==NULL)
3362: memset(&cur_tm,0,sizeof(cur_tm));
3363:
3364: /* ASCII Index File */
3365: if(startup->options&FTP_OPT_INDEX_FILE && startup->index_file_name[0]
3366: && (!stricmp(p,startup->index_file_name) || *p==0 || *p=='*')) {
3367: if(detail)
3368: fprintf(fp,"-r--r--r-- 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
3369: ,NAME_LEN
3370: ,scfg.sys_id
3371: ,lib<0 ? scfg.sys_id : dir<0
3372: ? scfg.lib[lib]->sname : scfg.dir[dir]->code_suffix
3373: ,512L
3374: ,ftp_mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
3375: ,startup->index_file_name);
3376: else
3377: fprintf(fp,"%s\r\n",startup->index_file_name);
3378: }
3379: /* HTML Index File */
3380: if(startup->options&FTP_OPT_HTML_INDEX_FILE && startup->html_index_file[0]
3381: && (!stricmp(p,startup->html_index_file) || *p==0 || *p=='*')) {
3382: if(detail)
3383: fprintf(fp,"-r--r--r-- 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
3384: ,NAME_LEN
3385: ,scfg.sys_id
3386: ,lib<0 ? scfg.sys_id : dir<0
3387: ? scfg.lib[lib]->sname : scfg.dir[dir]->code_suffix
3388: ,512L
3389: ,ftp_mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
3390: ,startup->html_index_file);
3391: else
3392: fprintf(fp,"%s\r\n",startup->html_index_file);
3393: }
3394:
3395: if(lib<0) { /* Root dir */
3396: lprintf(LOG_INFO,"%04d %s listing: root in %s mode",sock,user.alias, mode);
3397:
3398: /* QWK Packet */
3399: if(startup->options&FTP_OPT_ALLOW_QWK/* && fexist(qwkfile)*/) {
3400: if(detail) {
3401: if(fexistcase(qwkfile)) {
3402: t=fdate(qwkfile);
3403: l=flength(qwkfile);
3404: } else {
3405: t=time(NULL);
3406: l=10240;
3407: };
3408: if(localtime_r(&t,&tm)==NULL)
3409: memset(&tm,0,sizeof(tm));
3410: fprintf(fp,"-r--r--r-- 1 %-*s %-8s %9ld %s %2d %02d:%02d %s.qwk\r\n"
3411: ,NAME_LEN
3412: ,scfg.sys_id
3413: ,scfg.sys_id
3414: ,l
3415: ,ftp_mon[tm.tm_mon],tm.tm_mday,tm.tm_hour,tm.tm_min
3416: ,scfg.sys_id);
3417: } else
3418: fprintf(fp,"%s.qwk\r\n",scfg.sys_id);
3419: }
3420:
3421: /* File Aliases */
3422: sprintf(aliasfile,"%sftpalias.cfg",scfg.ctrl_dir);
3423: if((alias_fp=fopen(aliasfile,"r"))!=NULL) {
3424:
3425: while(!feof(alias_fp)) {
3426: if(!fgets(aliasline,sizeof(aliasline),alias_fp))
3427: break;
3428:
3429: alias_dir=FALSE;
3430:
3431: p=aliasline; /* alias pointer */
3432: while(*p && *p<=' ') p++;
3433:
3434: if(*p==';') /* comment */
3435: continue;
3436:
3437: tp=p; /* terminator pointer */
3438: while(*tp && *tp>' ') tp++;
3439: if(*tp) *tp=0;
3440:
3441: np=tp+1; /* filename pointer */
3442: while(*np && *np<=' ') np++;
3443:
3444: tp=np; /* terminator pointer */
3445: while(*tp && *tp>' ') tp++;
3446: if(*tp) *tp=0;
3447:
3448: dp=tp+1; /* description pointer */
3449: while(*dp && *dp<=' ') dp++;
3450: truncsp(dp);
3451:
3452: if(stricmp(dp,BBS_HIDDEN_ALIAS)==0)
3453: continue;
3454:
3455: /* Virtual Path? */
3456: if(!strnicmp(np,BBS_VIRTUAL_PATH,strlen(BBS_VIRTUAL_PATH))) {
3457: if((dir=getdir(np+strlen(BBS_VIRTUAL_PATH),&user))<0)
3458: continue; /* No access or invalid virtual path */
3459: tp=strrchr(np,'/');
3460: if(tp==NULL)
3461: continue;
3462: tp++;
3463: if(*tp) {
3464: SAFEPRINTF2(aliasfile,"%s%s",scfg.dir[dir]->path,tp);
3465: np=aliasfile;
3466: }
3467: else
3468: alias_dir=TRUE;
3469: }
3470:
3471: if(!alias_dir && !fexist(np))
3472: continue;
3473:
3474: if(detail) {
3475:
3476: if(alias_dir==TRUE) {
3477: fprintf(fp,"drwxrwxrwx 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
3478: ,NAME_LEN
3479: ,scfg.sys_id
3480: ,scfg.lib[scfg.dir[dir]->lib]->sname
3481: ,512L
3482: ,ftp_mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
3483: ,p);
3484: }
3485: else {
3486: t=fdate(np);
3487: if(localtime_r(&t,&tm)==NULL)
3488: memset(&tm,0,sizeof(tm));
3489: fprintf(fp,"-r--r--r-- 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
3490: ,NAME_LEN
3491: ,scfg.sys_id
3492: ,scfg.sys_id
3493: ,flength(np)
3494: ,ftp_mon[tm.tm_mon],tm.tm_mday,tm.tm_hour,tm.tm_min
3495: ,p);
3496: }
3497: } else
3498: fprintf(fp,"%s\r\n",p);
3499:
3500: }
3501:
3502: fclose(alias_fp);
3503: }
3504:
3505: /* Library folders */
3506: for(i=0;i<scfg.total_libs;i++) {
3507: if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
3508: continue;
3509: if(detail)
3510: fprintf(fp,"dr-xr-xr-x 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
3511: ,NAME_LEN
3512: ,scfg.sys_id
3513: ,scfg.sys_id
3514: ,512L
3515: ,ftp_mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
3516: ,scfg.lib[i]->sname);
3517: else
3518: fprintf(fp,"%s\r\n",scfg.lib[i]->sname);
3519: }
3520: } else if(dir<0) {
3521: lprintf(LOG_INFO,"%04d %s listing: %s library in %s mode"
3522: ,sock,user.alias,scfg.lib[lib]->sname,mode);
3523: for(i=0;i<scfg.total_dirs;i++) {
3524: if(scfg.dir[i]->lib!=lib)
3525: continue;
3526: if(i!=scfg.sysop_dir && i!=scfg.upload_dir
3527: && !chk_ar(&scfg,scfg.dir[i]->ar,&user))
3528: continue;
3529: if(detail)
3530: fprintf(fp,"drwxrwxrwx 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
3531: ,NAME_LEN
3532: ,scfg.sys_id
3533: ,scfg.lib[lib]->sname
3534: ,512L
3535: ,ftp_mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
3536: ,scfg.dir[i]->code_suffix);
3537: else
3538: fprintf(fp,"%s\r\n",scfg.dir[i]->code_suffix);
3539: }
3540: } else if(chk_ar(&scfg,scfg.dir[dir]->ar,&user)) {
3541: lprintf(LOG_INFO,"%04d %s listing: %s/%s directory in %s mode"
3542: ,sock,user.alias,scfg.lib[lib]->sname,scfg.dir[dir]->code_suffix,mode);
3543:
3544: SAFEPRINTF2(path,"%s%s",scfg.dir[dir]->path,*p ? p : "*");
3545: glob(path,0,NULL,&g);
3546: for(i=0;i<(int)g.gl_pathc;i++) {
3547: if(isdir(g.gl_pathv[i]))
3548: continue;
3549: #ifdef _WIN32
3550: GetShortPathName(g.gl_pathv[i], str, sizeof(str));
3551: #else
3552: SAFECOPY(str,g.gl_pathv[i]);
3553: #endif
3554: padfname(getfname(str),f.name);
3555: f.dir=dir;
3556: if((filedat=getfileixb(&scfg,&f))==FALSE
3557: && !(startup->options&FTP_OPT_DIR_FILES))
3558: continue;
3559: if(detail) {
3560: f.size=flength(g.gl_pathv[i]);
3561: getfiledat(&scfg,&f);
3562: t=fdate(g.gl_pathv[i]);
3563: if(localtime_r(&t,&tm)==NULL)
3564: memset(&tm,0,sizeof(tm));
3565: if(filedat) {
3566: if(f.misc&FM_ANON)
3567: SAFECOPY(str,ANONYMOUS);
3568: else
3569: dotname(f.uler,str);
3570: } else
3571: SAFECOPY(str,scfg.sys_id);
3572: fprintf(fp,"-r--r--r-- 1 %-*s %-8s %9ld %s %2d "
3573: ,NAME_LEN
3574: ,str
3575: ,scfg.dir[dir]->code_suffix
3576: ,f.size
3577: ,ftp_mon[tm.tm_mon],tm.tm_mday);
3578: if(tm.tm_year==cur_tm.tm_year)
3579: fprintf(fp,"%02d:%02d %s\r\n"
3580: ,tm.tm_hour,tm.tm_min
3581: ,getfname(g.gl_pathv[i]));
3582: else
3583: fprintf(fp,"%5d %s\r\n"
3584: ,1900+tm.tm_year
3585: ,getfname(g.gl_pathv[i]));
3586: } else
3587: fprintf(fp,"%s\r\n",getfname(g.gl_pathv[i]));
3588: }
3589: globfree(&g);
3590: } else
3591: lprintf(LOG_INFO,"%04d %s listing: %s/%s directory in %s mode (empty - no access)"
3592: ,sock,user.alias,scfg.lib[lib]->sname,scfg.dir[dir]->code_suffix,mode);
3593:
3594: fclose(fp);
3595: filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,0L
3596: ,&transfer_inprogress,&transfer_aborted
3597: ,TRUE /* delfile */
3598: ,TRUE /* tmpfile */
3599: ,&lastactive,&user,dir,FALSE,FALSE,FALSE,NULL);
3600: continue;
3601: }
3602:
3603: if(!strnicmp(cmd, "RETR ", 5)
3604: || !strnicmp(cmd, "SIZE ",5)
3605: || !strnicmp(cmd, "MDTM ",5)
3606: || !strnicmp(cmd, "DELE ",5)) {
3607: getdate=FALSE;
3608: getsize=FALSE;
3609: delecmd=FALSE;
3610: if(!strnicmp(cmd,"SIZE ",5))
3611: getsize=TRUE;
3612: else if(!strnicmp(cmd,"MDTM ",5))
3613: getdate=TRUE;
3614: else if(!strnicmp(cmd,"DELE ",5))
3615: delecmd=TRUE;
3616:
3617: if(!getsize && !getdate && user.rest&FLAG('D')) {
3618: sockprintf(sock,"550 Insufficient access.");
3619: filepos=0;
3620: continue;
3621: }
3622: credits=TRUE;
3623: success=FALSE;
3624: delfile=FALSE;
3625: tmpfile=FALSE;
3626: lib=curlib;
3627: dir=curdir;
3628:
3629: p=cmd+5;
3630: while(*p && *p<=' ') p++;
3631:
3632: if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR)))
3633: p+=strlen(BBS_FSYS_DIR); /* already mounted */
3634:
3635: if(*p=='/') {
3636: lib=-1;
3637: p++;
3638: }
3639: else if(!strncmp(p,"./",2))
3640: p+=2;
3641:
3642: if(lib<0 && ftpalias(p, fname, &user, &dir)==TRUE) {
3643: success=TRUE;
3644: credits=TRUE; /* include in d/l stats */
3645: tmpfile=FALSE;
3646: delfile=FALSE;
3647: lprintf(LOG_INFO,"%04d %s %.4s by alias: %s"
3648: ,sock,user.alias,cmd,p);
3649: p=getfname(fname);
3650: if(dir>=0)
3651: lib=scfg.dir[dir]->lib;
3652: }
3653: if(!success && lib<0 && (tp=strchr(p,'/'))!=NULL) {
3654: dir=-1;
3655: *tp=0;
3656: for(i=0;i<scfg.total_libs;i++) {
3657: if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
3658: continue;
3659: if(!stricmp(scfg.lib[i]->sname,p))
3660: break;
3661: }
3662: if(i<scfg.total_libs)
3663: lib=i;
3664: p=tp+1;
3665: }
3666: if(!success && dir<0 && (tp=strchr(p,'/'))!=NULL) {
3667: *tp=0;
3668: for(i=0;i<scfg.total_dirs;i++) {
3669: if(scfg.dir[i]->lib!=lib)
3670: continue;
3671: if(!chk_ar(&scfg,scfg.dir[i]->ar,&user))
3672: continue;
3673: if(!stricmp(scfg.dir[i]->code_suffix,p))
3674: break;
3675: }
3676: if(i<scfg.total_dirs)
3677: dir=i;
3678: p=tp+1;
3679: }
3680:
3681: sprintf(html_index_ext,"%s?",startup->html_index_file);
3682:
3683: sprintf(str,"%s.qwk",scfg.sys_id);
3684: if(lib<0 && startup->options&FTP_OPT_ALLOW_QWK
3685: && !stricmp(p,str) && !delecmd) {
3686: if(!fexistcase(qwkfile)) {
3687: lprintf(LOG_INFO,"%04d %s creating QWK packet...",sock,user.alias);
3688: sprintf(str,"%spack%04u.now",scfg.data_dir,user.number);
3689: if(!ftouch(str))
3690: lprintf(LOG_ERR,"%04d !ERROR creating semaphore file: %s"
3691: ,sock, str);
3692: t=time(NULL);
3693: while(fexist(str)) {
3694: if(time(NULL)-t>startup->qwk_timeout)
3695: break;
3696: mswait(1000);
3697: }
3698: if(fexist(str)) {
3699: lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for QWK packet creation",sock);
3700: sockprintf(sock,"451 Time-out waiting for packet creation.");
3701: remove(str);
3702: filepos=0;
3703: continue;
3704: }
3705: if(!fexistcase(qwkfile)) {
3706: lprintf(LOG_INFO,"%04d No QWK Packet created (no new messages)",sock);
3707: sockprintf(sock,"550 No QWK packet created (no new messages)");
3708: filepos=0;
3709: continue;
3710: }
3711: }
3712: SAFECOPY(fname,qwkfile);
3713: success=TRUE;
3714: delfile=TRUE;
3715: credits=FALSE;
3716: if(!getsize && !getdate)
3717: lprintf(LOG_INFO,"%04d %s downloading QWK packet (%lu bytes) in %s mode"
3718: ,sock,user.alias,flength(fname)
3719: ,mode);
3720: /* ASCII Index File */
3721: } else if(startup->options&FTP_OPT_INDEX_FILE
3722: && !stricmp(p,startup->index_file_name)
3723: && !delecmd) {
3724: if((fp=fopen(ftp_tmpfname(fname,sock),"w+b"))==NULL) {
3725: lprintf(LOG_ERR,"%04d !ERROR %d opening %s",sock,errno,fname);
3726: sockprintf(sock, "451 Insufficient system storage");
3727: filepos=0;
3728: continue;
3729: }
3730: if(!getsize && !getdate)
3731: lprintf(LOG_INFO,"%04d %s downloading index for %s in %s mode"
3732: ,sock,user.alias,vpath(lib,dir,str)
3733: ,mode);
3734: success=TRUE;
3735: credits=FALSE;
3736: tmpfile=TRUE;
3737: delfile=TRUE;
3738: fprintf(fp,"%-*s File/Folder Descriptions\r\n"
3739: ,INDEX_FNAME_LEN,startup->index_file_name);
3740: if(startup->options&FTP_OPT_HTML_INDEX_FILE)
3741: fprintf(fp,"%-*s File/Folder Descriptions (HTML)\r\n"
3742: ,INDEX_FNAME_LEN,startup->html_index_file);
3743: if(lib<0) {
3744:
3745: /* File Aliases */
3746: sprintf(aliasfile,"%sftpalias.cfg",scfg.ctrl_dir);
3747: if((alias_fp=fopen(aliasfile,"r"))!=NULL) {
3748:
3749: while(!feof(alias_fp)) {
3750: if(!fgets(aliasline,sizeof(aliasline),alias_fp))
3751: break;
3752:
3753: p=aliasline; /* alias pointer */
3754: while(*p && *p<=' ') p++;
3755:
3756: if(*p==';') /* comment */
3757: continue;
3758:
3759: tp=p; /* terminator pointer */
3760: while(*tp && *tp>' ') tp++;
3761: if(*tp) *tp=0;
3762:
3763: np=tp+1; /* filename pointer */
3764: while(*np && *np<=' ') np++;
3765:
3766: np++; /* description pointer */
3767: while(*np && *np>' ') np++;
3768:
3769: while(*np && *np<' ') np++;
3770:
3771: truncsp(np);
3772:
3773: fprintf(fp,"%-*s %s\r\n",INDEX_FNAME_LEN,p,np);
3774: }
3775:
3776: fclose(alias_fp);
3777: }
3778:
3779: /* QWK Packet */
3780: if(startup->options&FTP_OPT_ALLOW_QWK /* && fexist(qwkfile) */) {
3781: sprintf(str,"%s.qwk",scfg.sys_id);
3782: fprintf(fp,"%-*s QWK Message Packet\r\n"
3783: ,INDEX_FNAME_LEN,str);
3784: }
3785:
3786: /* Library Folders */
3787: for(i=0;i<scfg.total_libs;i++) {
3788: if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
3789: continue;
3790: fprintf(fp,"%-*s %s\r\n"
3791: ,INDEX_FNAME_LEN,scfg.lib[i]->sname,scfg.lib[i]->lname);
3792: }
3793: } else if(dir<0) {
3794: for(i=0;i<scfg.total_dirs;i++) {
3795: if(scfg.dir[i]->lib!=lib)
3796: continue;
3797: if(i!=scfg.sysop_dir && i!=scfg.upload_dir
3798: && !chk_ar(&scfg,scfg.dir[i]->ar,&user))
3799: continue;
3800: fprintf(fp,"%-*s %s\r\n"
3801: ,INDEX_FNAME_LEN,scfg.dir[i]->code_suffix,scfg.dir[i]->lname);
3802: }
3803: } else if(chk_ar(&scfg,scfg.dir[dir]->ar,&user)){
3804: sprintf(cmd,"%s*",scfg.dir[dir]->path);
3805: glob(cmd,0,NULL,&g);
3806: for(i=0;i<(int)g.gl_pathc;i++) {
3807: if(isdir(g.gl_pathv[i]))
3808: continue;
3809: #ifdef _WIN32
3810: GetShortPathName(g.gl_pathv[i], str, sizeof(str));
3811: #else
3812: SAFECOPY(str,g.gl_pathv[i]);
3813: #endif
3814: padfname(getfname(str),f.name);
3815: f.dir=dir;
3816: if(getfileixb(&scfg,&f)) {
3817: f.size=flength(g.gl_pathv[i]);
3818: getfiledat(&scfg,&f);
3819: fprintf(fp,"%-*s %s\r\n",INDEX_FNAME_LEN
3820: ,getfname(g.gl_pathv[i]),f.desc);
3821: }
3822: }
3823: globfree(&g);
3824: }
3825: fclose(fp);
3826: /* HTML Index File */
3827: } else if(startup->options&FTP_OPT_HTML_INDEX_FILE
3828: && (!stricmp(p,startup->html_index_file)
3829: || !strnicmp(p,html_index_ext,strlen(html_index_ext)))
3830: && !delecmd) {
3831: #ifdef JAVASCRIPT
3832: if(startup->options&FTP_OPT_NO_JAVASCRIPT) {
3833: lprintf(LOG_ERR,"%04d !JavaScript disabled, cannot generate %s",sock,fname);
3834: sockprintf(sock, "451 JavaScript disabled");
3835: filepos=0;
3836: continue;
3837: }
3838: if(js_runtime == NULL) {
3839: lprintf(LOG_DEBUG,"%04d JavaScript: Creating runtime: %lu bytes"
3840: ,sock,startup->js.max_bytes);
3841:
3842: if((js_runtime = JS_NewRuntime(startup->js.max_bytes))==NULL) {
3843: lprintf(LOG_ERR,"%04d !ERROR creating JavaScript runtime",sock);
3844: sockprintf(sock,"451 Error creating JavaScript runtime");
3845: filepos=0;
3846: continue;
3847: }
3848: }
3849:
3850: if(js_cx==NULL) { /* Context not yet created, create it now */
3851: if(((js_cx=js_initcx(js_runtime, sock,&js_glob,&js_ftp))==NULL)) {
3852: lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript context",sock);
3853: sockprintf(sock,"451 Error initializing JavaScript context");
3854: filepos=0;
3855: continue;
3856: }
3857: if(js_CreateUserClass(js_cx, js_glob, &scfg)==NULL)
3858: lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user class",sock);
3859:
3860: if(js_CreateFileClass(js_cx, js_glob)==NULL)
3861: lprintf(LOG_ERR,"%04d !JavaScript ERROR creating file class",sock);
3862:
3863: if(js_CreateUserObject(js_cx, js_glob, &scfg, "user", user.number)==NULL)
3864: lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user object",sock);
3865:
3866: if(js_CreateClientObject(js_cx, js_glob, "client", &client, sock)==NULL)
3867: lprintf(LOG_ERR,"%04d !JavaScript ERROR creating client object",sock);
3868:
3869: if(js_CreateFileAreaObject(js_cx, js_glob, &scfg, &user
3870: ,startup->html_index_file)==NULL)
3871: lprintf(LOG_ERR,"%04d !JavaScript ERROR creating file area object",sock);
3872: }
3873:
3874: if((js_str=JS_NewStringCopyZ(js_cx, "name"))!=NULL) {
3875: js_val=STRING_TO_JSVAL(js_str);
3876: JS_SetProperty(js_cx, js_ftp, "sort", &js_val);
3877: }
3878: js_val=BOOLEAN_TO_JSVAL(FALSE);
3879: JS_SetProperty(js_cx, js_ftp, "reverse", &js_val);
3880:
3881: if(!strnicmp(p,html_index_ext,strlen(html_index_ext))) {
3882: p+=strlen(html_index_ext);
3883: tp=strrchr(p,'$');
3884: if(tp!=NULL)
3885: *tp=0;
3886: if(!strnicmp(p,"ext=",4)) {
3887: p+=4;
3888: if(!strcmp(p,"on"))
3889: user.misc|=EXTDESC;
3890: else
3891: user.misc&=~EXTDESC;
3892: if(!(user.rest&FLAG('G')))
3893: putuserrec(&scfg,user.number,U_MISC,8,ultoa(user.misc,str,16));
3894: }
3895: else if(!strnicmp(p,"sort=",5)) {
3896: p+=5;
3897: tp=strchr(p,'&');
3898: if(tp!=NULL) {
3899: *tp=0;
3900: tp++;
3901: if(!stricmp(tp,"reverse")) {
3902: js_val=BOOLEAN_TO_JSVAL(TRUE);
3903: JS_SetProperty(js_cx, js_ftp, "reverse", &js_val);
3904: }
3905: }
3906: if((js_str=JS_NewStringCopyZ(js_cx, p))!=NULL) {
3907: js_val=STRING_TO_JSVAL(js_str);
3908: JS_SetProperty(js_cx, js_ftp, "sort", &js_val);
3909: }
3910: }
3911: }
3912:
3913: js_val=BOOLEAN_TO_JSVAL(INT_TO_BOOL(user.misc&EXTDESC));
3914: JS_SetProperty(js_cx, js_ftp, "extended_descriptions", &js_val);
3915:
3916: #endif
3917: if((fp=fopen(ftp_tmpfname(fname,sock),"w+b"))==NULL) {
3918: lprintf(LOG_ERR,"%04d !ERROR %d opening %s",sock,errno,fname);
3919: sockprintf(sock, "451 Insufficient system storage");
3920: filepos=0;
3921: continue;
3922: }
3923: if(!getsize && !getdate)
3924: lprintf(LOG_INFO,"%04d %s downloading HTML index for %s in %s mode"
3925: ,sock,user.alias,vpath(lib,dir,str)
3926: ,mode);
3927: success=TRUE;
3928: credits=FALSE;
3929: tmpfile=TRUE;
3930: delfile=TRUE;
3931: #ifdef JAVASCRIPT
3932: js_val=INT_TO_JSVAL(timeleft);
3933: if(!JS_SetProperty(js_cx, js_ftp, "time_left", &js_val))
3934: lprintf(LOG_ERR,"%04d !JavaScript ERROR setting user.time_left",sock);
3935: js_generate_index(js_cx, js_ftp, sock, fp, lib, dir, &user);
3936: #endif
3937: fclose(fp);
3938: } else if(dir>=0) {
3939:
3940: if(!chk_ar(&scfg,scfg.dir[dir]->ar,&user)) {
3941: lprintf(LOG_WARNING,"%04d !%s has insufficient access to /%s/%s"
3942: ,sock,user.alias
3943: ,scfg.lib[scfg.dir[dir]->lib]->sname
3944: ,scfg.dir[dir]->code_suffix);
3945: sockprintf(sock,"550 Insufficient access.");
3946: filepos=0;
3947: continue;
3948: }
3949:
3950: if(!getsize && !getdate && !delecmd
3951: && !chk_ar(&scfg,scfg.dir[dir]->dl_ar,&user)) {
3952: lprintf(LOG_WARNING,"%04d !%s has insufficient access to download from /%s/%s"
3953: ,sock,user.alias
3954: ,scfg.lib[scfg.dir[dir]->lib]->sname
3955: ,scfg.dir[dir]->code_suffix);
3956: sockprintf(sock,"550 Insufficient access.");
3957: filepos=0;
3958: continue;
3959: }
3960:
3961: if(delecmd && !dir_op(&scfg,&user,dir)) {
3962: lprintf(LOG_WARNING,"%04d !%s has insufficient access to delete files in /%s/%s"
3963: ,sock,user.alias
3964: ,scfg.lib[scfg.dir[dir]->lib]->sname
3965: ,scfg.dir[dir]->code_suffix);
3966: sockprintf(sock,"550 Insufficient access.");
3967: filepos=0;
3968: continue;
3969: }
3970: SAFEPRINTF2(fname,"%s%s",scfg.dir[dir]->path,p);
3971: #ifdef _WIN32
3972: GetShortPathName(fname, str, sizeof(str));
3973: #else
3974: SAFECOPY(str,fname);
3975: #endif
3976: padfname(getfname(str),f.name);
3977: f.dir=dir;
3978: f.cdt=0;
3979: f.size=-1;
3980: filedat=getfileixb(&scfg,&f);
3981: if(!filedat && !(startup->options&FTP_OPT_DIR_FILES)) {
3982: sockprintf(sock,"550 File not found: %s",p);
3983: lprintf(LOG_WARNING,"%04d !%s file (%s%s) not in database for %.4s command"
3984: ,sock,user.alias,vpath(lib,dir,str),p,cmd);
3985: filepos=0;
3986: continue;
3987: }
3988:
3989: /* Verify credits */
3990: if(!getsize && !getdate && !delecmd
3991: && !is_download_free(&scfg,dir,&user)) {
3992: if(filedat)
3993: getfiledat(&scfg,&f);
3994: else
3995: f.cdt=flength(fname);
3996: if(f.cdt>(user.cdt+user.freecdt)) {
3997: lprintf(LOG_WARNING,"%04d !%s has insufficient credit to download /%s/%s/%s (%lu credits)"
3998: ,sock,user.alias,scfg.lib[scfg.dir[dir]->lib]->sname
3999: ,scfg.dir[dir]->code_suffix
4000: ,p
4001: ,f.cdt);
4002: sockprintf(sock,"550 Insufficient credit (%lu required).",f.cdt);
4003: filepos=0;
4004: continue;
4005: }
4006: }
4007:
4008: if(strcspn(p,ILLEGAL_FILENAME_CHARS)!=strlen(p)) {
4009: success=FALSE;
4010: lprintf(LOG_WARNING,"%04d !ILLEGAL FILENAME ATTEMPT by %s: %s"
4011: ,sock,user.alias,p);
4012: hacklog(&scfg, "FTP", user.alias, cmd, host_name, &ftp.client_addr);
4013: #ifdef _WIN32
4014: if(startup->hack_sound[0] && !(startup->options&FTP_OPT_MUTE))
4015: PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME);
4016: #endif
4017: } else {
4018: if(fexist(fname)) {
4019: success=TRUE;
4020: if(!getsize && !getdate && !delecmd)
4021: lprintf(LOG_INFO,"%04d %s downloading: %s (%lu bytes) in %s mode"
4022: ,sock,user.alias,fname,flength(fname)
4023: ,mode);
4024: }
4025: }
4026: }
4027: #if defined(SOCKET_DEBUG_DOWNLOAD)
4028: socket_debug[sock]|=SOCKET_DEBUG_DOWNLOAD;
4029: #endif
4030:
4031: if(getsize && success)
4032: sockprintf(sock,"213 %lu",flength(fname));
4033: else if(getdate && success) {
4034: t=fdate(fname);
4035: if(gmtime_r(&t,&tm)==NULL) /* specifically use GMT/UTC representation */
4036: memset(&tm,0,sizeof(tm));
4037: sockprintf(sock,"213 %u%02u%02u%02u%02u%02u"
4038: ,1900+tm.tm_year,tm.tm_mon+1,tm.tm_mday
4039: ,tm.tm_hour,tm.tm_min,tm.tm_sec);
4040: } else if(delecmd && success) {
4041: if(remove(fname)!=0) {
4042: lprintf(LOG_ERR,"%04d !ERROR %d deleting %s",sock,errno,fname);
4043: sockprintf(sock,"450 %s could not be deleted (error: %d)"
4044: ,fname,errno);
4045: } else {
4046: lprintf(LOG_NOTICE,"%04d %s deleted %s",sock,user.alias,fname);
4047: if(filedat)
4048: removefiledat(&scfg,&f);
4049: sockprintf(sock,"250 %s deleted.",fname);
4050: }
4051: } else if(success) {
4052: sockprintf(sock,"150 Opening BINARY mode data connection for file transfer.");
4053: filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,filepos
4054: ,&transfer_inprogress,&transfer_aborted,delfile,tmpfile
4055: ,&lastactive,&user,dir,FALSE,credits,FALSE,NULL);
4056: }
4057: else {
4058: sockprintf(sock,"550 File not found: %s",p);
4059: lprintf(LOG_WARNING,"%04d !%s file (%s%s) not found for %.4s command"
4060: ,sock,user.alias,vpath(lib,dir,str),p,cmd);
4061: }
4062: filepos=0;
4063: #if defined(SOCKET_DEBUG_DOWNLOAD)
4064: socket_debug[sock]&=~SOCKET_DEBUG_DOWNLOAD;
4065: #endif
4066: continue;
4067: }
4068:
4069: if(!strnicmp(cmd, "DESC", 4)) {
4070:
4071: if(user.rest&FLAG('U')) {
4072: sockprintf(sock,"553 Insufficient access.");
4073: continue;
4074: }
4075:
4076: p=cmd+4;
4077: while(*p && *p<=' ') p++;
4078:
4079: if(*p==0)
4080: sockprintf(sock,"501 No file description given.");
4081: else {
4082: SAFECOPY(desc,p);
4083: sockprintf(sock,"200 File description set. Ready to STOR file.");
4084: }
4085: continue;
4086: }
4087:
4088: if(!strnicmp(cmd, "STOR ", 5) || !strnicmp(cmd, "APPE ", 5)) {
4089:
4090: if(user.rest&FLAG('U')) {
4091: sockprintf(sock,"553 Insufficient access.");
4092: continue;
4093: }
4094:
4095: if(transfer_inprogress==TRUE) {
4096: lprintf(LOG_WARNING,"%04d !TRANSFER already in progress (%s)",sock,cmd);
4097: sockprintf(sock,"425 Transfer already in progress.");
4098: continue;
4099: }
4100:
4101: append=FALSE;
4102: lib=curlib;
4103: dir=curdir;
4104: p=cmd+5;
4105:
4106: while(*p && *p<=' ') p++;
4107:
4108: if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR)))
4109: p+=strlen(BBS_FSYS_DIR); /* already mounted */
4110:
4111: if(*p=='/') {
4112: lib=-1;
4113: p++;
4114: }
4115: else if(!strncmp(p,"./",2))
4116: p+=2;
4117: /* Need to add support for uploading to aliased directories */
4118: if(lib<0 && (tp=strchr(p,'/'))!=NULL) {
4119: dir=-1;
4120: *tp=0;
4121: for(i=0;i<scfg.total_libs;i++) {
4122: if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
4123: continue;
4124: if(!stricmp(scfg.lib[i]->sname,p))
4125: break;
4126: }
4127: if(i<scfg.total_libs)
4128: lib=i;
4129: p=tp+1;
4130: }
4131: if(dir<0 && (tp=strchr(p,'/'))!=NULL) {
4132: *tp=0;
4133: for(i=0;i<scfg.total_dirs;i++) {
4134: if(scfg.dir[i]->lib!=lib)
4135: continue;
4136: if(i!=scfg.sysop_dir && i!=scfg.upload_dir
4137: && !chk_ar(&scfg,scfg.dir[i]->ar,&user))
4138: continue;
4139: if(!stricmp(scfg.dir[i]->code_suffix,p))
4140: break;
4141: }
4142: if(i<scfg.total_dirs)
4143: dir=i;
4144: p=tp+1;
4145: }
4146: if(dir<0) {
4147: sprintf(str,"%s.rep",scfg.sys_id);
4148: if(!(startup->options&FTP_OPT_ALLOW_QWK)
4149: || stricmp(p,str)) {
4150: lprintf(LOG_WARNING,"%04d !%s attempted to upload to invalid directory"
4151: ,sock,user.alias);
4152: sockprintf(sock,"553 Invalid directory.");
4153: continue;
4154: }
4155: sprintf(fname,"%sfile/%04d.rep",scfg.data_dir,user.number);
4156: lprintf(LOG_INFO,"%04d %s uploading: %s in %s mode"
4157: ,sock,user.alias,fname
4158: ,mode);
4159: } else {
4160:
4161: append=(strnicmp(cmd,"APPE",4)==0);
4162:
4163: if(!chk_ar(&scfg,scfg.dir[dir]->ul_ar,&user)) {
4164: lprintf(LOG_WARNING,"%04d !%s has insufficient access to upload to /%s/%s"
4165: ,sock,user.alias
4166: ,scfg.lib[scfg.dir[dir]->lib]->sname
4167: ,scfg.dir[dir]->code_suffix);
4168: sockprintf(sock,"553 Insufficient access.");
4169: continue;
4170: }
4171: if(*p=='-'
4172: || strcspn(p,ILLEGAL_FILENAME_CHARS)!=strlen(p)
4173: || trashcan(&scfg,p,"file")) {
4174: lprintf(LOG_WARNING,"%04d !ILLEGAL FILENAME ATTEMPT by %s: %s"
4175: ,sock,user.alias,p);
4176: sockprintf(sock,"553 Illegal filename attempt");
4177: hacklog(&scfg, "FTP", user.alias, cmd, host_name, &ftp.client_addr);
4178: #ifdef _WIN32
4179: if(startup->hack_sound[0] && !(startup->options&FTP_OPT_MUTE))
4180: PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME);
4181: #endif
4182: continue;
4183: }
4184: SAFEPRINTF2(fname,"%s%s",scfg.dir[dir]->path,p);
4185: if((!append && filepos==0 && fexist(fname))
4186: || (startup->options&FTP_OPT_INDEX_FILE
4187: && !stricmp(p,startup->index_file_name))
4188: || (startup->options&FTP_OPT_HTML_INDEX_FILE
4189: && !stricmp(p,startup->html_index_file))
4190: ) {
4191: lprintf(LOG_WARNING,"%04d !%s attempted to overwrite existing file: %s"
4192: ,sock,user.alias,fname);
4193: sockprintf(sock,"553 File already exists.");
4194: continue;
4195: }
4196: if(append || filepos) { /* RESUME */
4197: #ifdef _WIN32
4198: GetShortPathName(fname, str, sizeof(str));
4199: #else
4200: SAFECOPY(str,fname);
4201: #endif
4202: padfname(getfname(str),f.name);
4203: f.dir=dir;
4204: f.cdt=0;
4205: f.size=-1;
4206: if(!getfileixb(&scfg,&f) || !getfiledat(&scfg,&f)) {
4207: if(filepos) {
4208: lprintf(LOG_WARNING,"%04d !%s file (%s) not in database for %.4s command"
4209: ,sock,user.alias,fname,cmd);
4210: sockprintf(sock,"550 File not found: %s",p);
4211: continue;
4212: }
4213: append=FALSE;
4214: }
4215: /* Verify user is original uploader */
4216: if((append || filepos) && stricmp(f.uler,user.alias)) {
4217: lprintf(LOG_WARNING,"%04d !%s cannot resume upload of %s, uploaded by %s"
4218: ,sock,user.alias,fname,f.uler);
4219: sockprintf(sock,"553 Insufficient access (can't resume upload from different user).");
4220: continue;
4221: }
4222: }
4223: lprintf(LOG_INFO,"%04d %s uploading: %s to %s (%s) in %s mode"
4224: ,sock,user.alias
4225: ,p /* filename */
4226: ,vpath(lib,dir,str) /* virtual path */
4227: ,scfg.dir[dir]->path /* actual path */
4228: ,mode);
4229: }
4230: sockprintf(sock,"150 Opening BINARY mode data connection for file transfer.");
4231: filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,filepos
4232: ,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
4233: ,&lastactive
4234: ,&user
4235: ,dir
4236: ,TRUE /* uploading */
4237: ,TRUE /* credits */
4238: ,append
4239: ,desc
4240: );
4241: filepos=0;
4242: continue;
4243: }
4244:
4245: if(!stricmp(cmd,"CDUP") || !stricmp(cmd,"XCUP")) {
4246: if(curdir<0)
4247: curlib=-1;
4248: else
4249: curdir=-1;
4250: sockprintf(sock,"200 CDUP command successful.");
4251: continue;
4252: }
4253:
4254: if(!strnicmp(cmd, "CWD ", 4) || !strnicmp(cmd,"XCWD ",5)) {
4255: p=cmd+4;
4256: while(*p && *p<=' ') p++;
4257:
4258: if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR)))
4259: p+=strlen(BBS_FSYS_DIR); /* already mounted */
4260:
4261: if(*p=='/') {
4262: curlib=-1;
4263: curdir=-1;
4264: p++;
4265: }
4266: /* Local File System? */
4267: if(sysop && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)
4268: && !strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) {
4269: p+=strlen(LOCAL_FSYS_DIR);
4270: if(!direxist(p)) {
4271: sockprintf(sock,"550 Directory does not exist.");
4272: lprintf(LOG_WARNING,"%04d !%s attempted to mount invalid directory: %s"
4273: ,sock, user.alias, p);
4274: continue;
4275: }
4276: SAFECOPY(local_dir,p);
4277: local_fsys=TRUE;
4278: sockprintf(sock,"250 CWD command successful (local file system mounted).");
4279: lprintf(LOG_INFO,"%04d %s mounted local file system", sock, user.alias);
4280: continue;
4281: }
4282: success=FALSE;
4283:
4284: /* Directory Alias? */
4285: if(curlib<0 && ftpalias(p,NULL,&user,&curdir)==TRUE) {
4286: if(curdir>=0)
4287: curlib=scfg.dir[curdir]->lib;
4288: success=TRUE;
4289: }
4290:
4291: orglib=curlib;
4292: orgdir=curdir;
4293: tp=0;
4294: if(!strncmp(p,"...",3)) {
4295: curlib=-1;
4296: curdir=-1;
4297: p+=3;
4298: }
4299: if(!strncmp(p,"./",2))
4300: p+=2;
4301: else if(!strncmp(p,"..",2)) {
4302: if(curdir<0)
4303: curlib=-1;
4304: else
4305: curdir=-1;
4306: p+=2;
4307: }
4308: if(*p==0)
4309: success=TRUE;
4310: else if(!strcmp(p,"."))
4311: success=TRUE;
4312: if(!success && (curlib<0 || *p=='/')) { /* Root dir */
4313: if(*p=='/') p++;
4314: tp=strchr(p,'/');
4315: if(tp) *tp=0;
4316: for(i=0;i<scfg.total_libs;i++) {
4317: if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
4318: continue;
4319: if(!stricmp(scfg.lib[i]->sname,p))
4320: break;
4321: }
4322: if(i<scfg.total_libs) {
4323: curlib=i;
4324: success=TRUE;
4325: }
4326: }
4327: if((!success && curdir<0) || (success && tp && *(tp+1))) {
4328: if(tp)
4329: p=tp+1;
4330: tp=lastchar(p);
4331: if(tp && *tp=='/') *tp=0;
4332: for(i=0;i<scfg.total_dirs;i++) {
4333: if(scfg.dir[i]->lib!=curlib)
4334: continue;
4335: if(i!=scfg.sysop_dir && i!=scfg.upload_dir
4336: && !chk_ar(&scfg,scfg.dir[i]->ar,&user))
4337: continue;
4338: if(!stricmp(scfg.dir[i]->code_suffix,p))
4339: break;
4340: }
4341: if(i<scfg.total_dirs) {
4342: curdir=i;
4343: success=TRUE;
4344: } else
4345: success=FALSE;
4346: }
4347:
4348: if(success)
4349: sockprintf(sock,"250 CWD command successful.");
4350: else {
4351: sockprintf(sock,"550 %s: No such file or directory.",p);
4352: curlib=orglib;
4353: curdir=orgdir;
4354: }
4355: continue;
4356: }
4357:
4358: if(!stricmp(cmd, "PWD") || !stricmp(cmd,"XPWD")) {
4359: if(curlib<0)
4360: sockprintf(sock,"257 \"/\" is current directory.");
4361: else if(curdir<0)
4362: sockprintf(sock,"257 \"/%s\" is current directory."
4363: ,scfg.lib[curlib]->sname);
4364: else
4365: sockprintf(sock,"257 \"/%s/%s\" is current directory."
4366: ,scfg.lib[curlib]->sname
4367: ,scfg.dir[curdir]->code_suffix);
4368: continue;
4369: }
4370:
4371: if(!strnicmp(cmd, "MKD", 3) ||
4372: !strnicmp(cmd,"XMKD",4) ||
4373: !strnicmp(cmd,"SITE EXEC",9)) {
4374: lprintf(LOG_WARNING,"%04d !SUSPECTED HACK ATTEMPT by %s: '%s'"
4375: ,sock,user.alias,cmd);
4376: hacklog(&scfg, "FTP", user.alias, cmd, host_name, &ftp.client_addr);
4377: #ifdef _WIN32
4378: if(startup->hack_sound[0] && !(startup->options&FTP_OPT_MUTE))
4379: PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME);
4380: #endif
4381: }
4382: sockprintf(sock,"500 Syntax error: '%s'",cmd);
4383: lprintf(LOG_WARNING,"%04d !UNSUPPORTED COMMAND from %s: '%s'"
4384: ,sock,user.alias,cmd);
4385: } /* while(1) */
4386:
4387: #if defined(SOCKET_DEBUG_TERMINATE)
4388: socket_debug[sock]|=SOCKET_DEBUG_TERMINATE;
4389: #endif
4390:
4391: if(transfer_inprogress==TRUE) {
4392: lprintf(LOG_DEBUG,"%04d Waiting for transfer to complete...",sock);
4393: count=0;
4394: while(transfer_inprogress==TRUE) {
4395: if(server_socket==INVALID_SOCKET || terminate_server) {
4396: mswait(2000); /* allow xfer threads to terminate */
4397: break;
4398: }
4399: if(!transfer_aborted) {
4400: if(gettimeleft(&scfg,&user,logintime)<1) {
4401: lprintf(LOG_WARNING,"%04d Out of time, disconnecting",sock);
4402: sockprintf(sock,"421 Sorry, you've run out of time.");
4403: ftp_close_socket(&data_sock,__LINE__);
4404: transfer_aborted=TRUE;
4405: }
4406: if((time(NULL)-lastactive)>startup->max_inactivity) {
4407: lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",sock);
4408: sockprintf(sock,"421 Disconnecting due to inactivity (%u seconds)."
4409: ,startup->max_inactivity);
4410: ftp_close_socket(&data_sock,__LINE__);
4411: transfer_aborted=TRUE;
4412: }
4413: }
4414: if(count && (count%60)==0)
4415: lprintf(LOG_WARNING,"%04d Still waiting for transfer to complete "
4416: "(count=%lu, aborted=%d, lastactive=%lX) ..."
4417: ,sock,count,transfer_aborted,lastactive);
4418: count++;
4419: mswait(1000);
4420: }
4421: lprintf(LOG_DEBUG,"%04d Done waiting for transfer to complete",sock);
4422: }
4423:
4424: if(user.number) {
4425: /* Update User Statistics */
4426: logoutuserdat(&scfg, &user, time(NULL), logintime);
4427: /* Remove QWK-pack semaphore file (if left behind) */
4428: sprintf(str,"%spack%04u.now",scfg.data_dir,user.number);
4429: remove(str);
4430: lprintf(LOG_INFO,"%04d %s logged off",sock,user.alias);
4431: }
4432:
4433: #ifdef _WIN32
4434: if(startup->hangup_sound[0] && !(startup->options&FTP_OPT_MUTE))
4435: PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME);
4436: #endif
4437:
4438: #ifdef JAVASCRIPT
4439: if(js_cx!=NULL) {
4440: lprintf(LOG_DEBUG,"%04d JavaScript: Destroying context",sock);
4441: JS_DestroyContext(js_cx); /* Free Context */
4442: }
4443:
4444: if(js_runtime!=NULL) {
4445: lprintf(LOG_DEBUG,"%04d JavaScript: Destroying runtime",sock);
4446: JS_DestroyRuntime(js_runtime);
4447: }
4448:
4449: #endif
4450:
4451: /* status(STATUS_WFC); server thread should control status display */
4452:
4453: if(pasv_sock!=INVALID_SOCKET)
4454: ftp_close_socket(&pasv_sock,__LINE__);
4455: if(data_sock!=INVALID_SOCKET)
4456: ftp_close_socket(&data_sock,__LINE__);
4457:
4458: client_off(sock);
4459:
4460: #ifdef SOCKET_DEBUG_CTRL
4461: socket_debug[sock]&=~SOCKET_DEBUG_CTRL;
4462: #endif
4463:
4464: #if defined(SOCKET_DEBUG_TERMINATE)
4465: socket_debug[sock]&=~SOCKET_DEBUG_TERMINATE;
4466: #endif
4467:
4468: tmp_sock=sock;
4469: ftp_close_socket(&tmp_sock,__LINE__);
4470:
4471: if(active_clients)
4472: active_clients--, update_clients();
4473:
4474: thread_down();
4475: lprintf(LOG_DEBUG,"%04d CTRL thread terminated (%d clients, %u threads remain, %lu served)"
4476: ,sock, active_clients, thread_count, served);
4477: }
4478:
4479: static void cleanup(int code, int line)
4480: {
4481: #ifdef _DEBUG
4482: lprintf(LOG_INFO,"0000 cleanup called from line %d",line);
4483: #endif
4484: free_cfg(&scfg);
4485: free_text(text);
4486:
4487: semfile_list_free(&recycle_semfiles);
4488: semfile_list_free(&shutdown_semfiles);
4489:
4490: if(server_socket!=INVALID_SOCKET)
4491: ftp_close_socket(&server_socket,__LINE__);
4492:
4493: update_clients();
4494:
4495: #ifdef _WINSOCKAPI_
4496: if(WSAInitialized && WSACleanup()!=0)
4497: lprintf(LOG_ERR,"0000 !WSACleanup ERROR %d",ERROR_VALUE);
4498: #endif
4499:
4500: thread_down();
4501: status("Down");
4502: if(terminate_server || code)
4503: lprintf(LOG_INFO,"#### FTP Server thread terminated (%u threads remain, %lu clients served)"
4504: ,thread_count, served);
4505: if(startup!=NULL && startup->terminated!=NULL)
4506: startup->terminated(startup->cbdata,code);
4507: }
4508:
4509: const char* DLLCALL ftp_ver(void)
4510: {
4511: static char ver[256];
4512: char compiler[32];
4513:
4514: DESCRIBE_COMPILER(compiler);
4515:
4516: sscanf("$Revision: 1.313 $", "%*s %s", revision);
4517:
4518: sprintf(ver,"%s %s%s "
4519: "Compiled %s %s with %s"
4520: ,FTP_SERVER
4521: ,revision
4522: #ifdef _DEBUG
4523: ," Debug"
4524: #else
4525: ,""
4526: #endif
4527: ,__DATE__, __TIME__, compiler);
4528:
4529: return(ver);
4530: }
4531:
4532: void DLLCALL ftp_server(void* arg)
4533: {
4534: char* p;
4535: char path[MAX_PATH+1];
4536: char error[256];
4537: char compiler[32];
4538: char str[256];
4539: SOCKADDR_IN server_addr;
4540: SOCKADDR_IN client_addr;
4541: socklen_t client_addr_len;
4542: SOCKET client_socket;
4543: int i;
4544: int result;
4545: time_t t;
4546: time_t start;
4547: time_t initialized=0;
4548: fd_set socket_set;
4549: ftp_t* ftp;
4550: struct timeval tv;
4551:
4552: ftp_ver();
4553:
4554: startup=(ftp_startup_t*)arg;
4555:
4556: #ifdef _THREAD_SUID_BROKEN
4557: if(thread_suid_broken)
4558: startup->seteuid(TRUE);
4559: #endif
4560:
4561: if(startup==NULL) {
4562: sbbs_beep(100,500);
4563: fprintf(stderr, "No startup structure passed!\n");
4564: return;
4565: }
4566:
4567: if(startup->size!=sizeof(ftp_startup_t)) { /* verify size */
4568: sbbs_beep(100,500);
4569: sbbs_beep(300,500);
4570: sbbs_beep(100,500);
4571: fprintf(stderr, "Invalid startup structure!\n");
4572: return;
4573: }
4574:
4575: /* Setup intelligent defaults */
4576: if(startup->port==0) startup->port=IPPORT_FTP;
4577: if(startup->qwk_timeout==0) startup->qwk_timeout=600; /* seconds */
4578: if(startup->max_inactivity==0) startup->max_inactivity=300; /* seconds */
4579: if(startup->sem_chk_freq==0) startup->sem_chk_freq=2; /* seconds */
4580: if(startup->index_file_name[0]==0) SAFECOPY(startup->index_file_name,"00index");
4581: if(startup->html_index_file[0]==0) SAFECOPY(startup->html_index_file,"00index.html");
4582: if(startup->html_index_script[0]==0) { SAFECOPY(startup->html_index_script,"ftp-html.js");
4583: startup->options|=FTP_OPT_HTML_INDEX_FILE;
4584: }
4585: if(startup->options&FTP_OPT_HTML_INDEX_FILE)
4586: startup->options&=~FTP_OPT_NO_JAVASCRIPT;
4587: else
4588: startup->options|=FTP_OPT_NO_JAVASCRIPT;
4589: #ifdef JAVASCRIPT
4590: if(startup->js.max_bytes==0) startup->js.max_bytes=JAVASCRIPT_MAX_BYTES;
4591: if(startup->js.cx_stack==0) startup->js.cx_stack=JAVASCRIPT_CONTEXT_STACK;
4592:
4593: ZERO_VAR(js_server_props);
4594: SAFEPRINTF2(js_server_props.version,"%s %s",FTP_SERVER,revision);
4595: js_server_props.version_detail=ftp_ver();
4596: js_server_props.clients=&active_clients;
4597: js_server_props.options=&startup->options;
4598: js_server_props.interface_addr=&startup->interface_addr;
4599: #endif
4600:
4601:
4602: uptime=0;
4603: served=0;
4604: startup->recycle_now=FALSE;
4605: startup->shutdown_now=FALSE;
4606: terminate_server=FALSE;
4607:
4608: do {
4609:
4610: thread_up(FALSE /* setuid */);
4611:
4612: status("Initializing");
4613:
4614: memset(&scfg, 0, sizeof(scfg));
4615:
4616: lprintf(LOG_INFO,"Synchronet FTP Server Revision %s%s"
4617: ,revision
4618: #ifdef _DEBUG
4619: ," Debug"
4620: #else
4621: ,""
4622: #endif
4623: );
4624:
4625: DESCRIBE_COMPILER(compiler);
4626:
4627: lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler);
4628:
4629: sbbs_srand(); /* Seed random number generator */
4630:
4631: if(!winsock_startup()) {
4632: cleanup(1,__LINE__);
4633: return;
4634: }
4635:
4636: t=time(NULL);
4637: lprintf(LOG_INFO,"Initializing on %.24s with options: %lx"
4638: ,CTIME_R(&t,str),startup->options);
4639:
4640: /* Initial configuration and load from CNF files */
4641: SAFECOPY(scfg.ctrl_dir, startup->ctrl_dir);
4642: lprintf(LOG_INFO,"Loading configuration files from %s", scfg.ctrl_dir);
4643: scfg.size=sizeof(scfg);
4644: SAFECOPY(error,UNKNOWN_LOAD_ERROR);
4645: if(!load_cfg(&scfg, text, TRUE, error)) {
4646: lprintf(LOG_ERR,"!ERROR %s",error);
4647: lprintf(LOG_ERR,"!Failed to load configuration files");
4648: cleanup(1,__LINE__);
4649: return;
4650: }
4651:
4652: if(startup->host_name[0]==0)
4653: SAFECOPY(startup->host_name,scfg.sys_inetaddr);
4654:
4655: if(!(scfg.sys_misc&SM_LOCAL_TZ) && !(startup->options&FTP_OPT_LOCAL_TIMEZONE)) {
4656: if(putenv("TZ=UTC0"))
4657: lprintf(LOG_ERR,"!putenv() FAILED");
4658: tzset();
4659:
4660: if((t=checktime())!=0) { /* Check binary time */
4661: lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t);
4662: cleanup(1,__LINE__);
4663: return;
4664: }
4665: }
4666:
4667: if(uptime==0)
4668: uptime=time(NULL); /* this must be done *after* setting the timezone */
4669:
4670: if(startup->temp_dir[0])
4671: SAFECOPY(scfg.temp_dir,startup->temp_dir);
4672: else
4673: SAFECOPY(scfg.temp_dir,"../temp");
4674: prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
4675: MKDIR(scfg.temp_dir);
4676: lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir);
4677: if(!isdir(scfg.temp_dir)) {
4678: lprintf(LOG_ERR,"!Invalid temp directory: %s", scfg.temp_dir);
4679: cleanup(1,__LINE__);
4680: return;
4681: }
4682:
4683: if(!startup->max_clients) {
4684: startup->max_clients=scfg.sys_nodes;
4685: if(startup->max_clients<10)
4686: startup->max_clients=10;
4687: }
4688: lprintf(LOG_DEBUG,"Maximum clients: %d",startup->max_clients);
4689:
4690: /* Sanity-check the passive port range */
4691: if(startup->pasv_port_low || startup->pasv_port_high) {
4692: if(startup->pasv_port_low > startup->pasv_port_high
4693: || startup->pasv_port_high-startup->pasv_port_low < (startup->max_clients-1)) {
4694: lprintf(LOG_WARNING,"!Correcting Passive Port Range (Low: %u, High: %u)"
4695: ,startup->pasv_port_low,startup->pasv_port_high);
4696: if(startup->pasv_port_low)
4697: startup->pasv_port_high = startup->pasv_port_low+(startup->max_clients-1);
4698: else
4699: startup->pasv_port_low = startup->pasv_port_high-(startup->max_clients-1);
4700: }
4701: lprintf(LOG_DEBUG,"Passive Port Low: %u",startup->pasv_port_low);
4702: lprintf(LOG_DEBUG,"Passive Port High: %u",startup->pasv_port_high);
4703: }
4704:
4705: lprintf(LOG_DEBUG,"Maximum inactivity: %d seconds",startup->max_inactivity);
4706:
4707: active_clients=0, update_clients();
4708:
4709: strlwr(scfg.sys_id); /* Use lower-case unix-looking System ID for group name */
4710:
4711: for(i=0;i<scfg.total_libs;i++) {
4712: strlwr(scfg.lib[i]->sname);
4713: dotname(scfg.lib[i]->sname,scfg.lib[i]->sname);
4714: }
4715: #if 0 /* this is now handled by load_cfg()->prep_cfg() */
4716: for(i=0;i<scfg.total_dirs;i++)
4717: strlwr(scfg.dir[i]->code_suffix);
4718: #endif
4719: /* open a socket and wait for a client */
4720:
4721: if((server_socket=ftp_open_socket(SOCK_STREAM))==INVALID_SOCKET) {
4722: lprintf(LOG_ERR,"!ERROR %d opening socket", ERROR_VALUE);
4723: cleanup(1,__LINE__);
4724: return;
4725: }
4726:
4727: lprintf(LOG_DEBUG,"%04d FTP Server socket opened",server_socket);
4728:
4729: /*****************************/
4730: /* Listen for incoming calls */
4731: /*****************************/
4732: memset(&server_addr, 0, sizeof(server_addr));
4733:
4734: server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
4735: server_addr.sin_family = AF_INET;
4736: server_addr.sin_port = htons(startup->port);
4737:
4738: if(startup->seteuid!=NULL)
4739: startup->seteuid(FALSE);
4740: result=retry_bind(server_socket, (struct sockaddr *) &server_addr,sizeof(server_addr)
4741: ,startup->bind_retry_count,startup->bind_retry_delay,"FTP Server",lprintf);
4742: if(startup->seteuid!=NULL)
4743: startup->seteuid(TRUE);
4744: if(result!=0) {
4745: lprintf(LOG_ERR,"%04d %s", server_socket, BIND_FAILURE_HELP);
4746: cleanup(1,__LINE__);
4747: return;
4748: }
4749:
4750: if((result=listen(server_socket, 1))!= 0) {
4751: lprintf(LOG_ERR,"%04d !ERROR %d (%d) listening on socket"
4752: ,server_socket, result, ERROR_VALUE);
4753: cleanup(1,__LINE__);
4754: return;
4755: }
4756:
4757: lprintf(LOG_NOTICE,"%04d FTP Server thread started on port %d",server_socket,startup->port);
4758: status(STATUS_WFC);
4759:
4760: /* Setup recycle/shutdown semaphore file lists */
4761: shutdown_semfiles=semfile_list_init(scfg.ctrl_dir,"shutdown","ftp");
4762: recycle_semfiles=semfile_list_init(scfg.ctrl_dir,"recycle","ftp");
4763: SAFEPRINTF(path,"%sftpsrvr.rec",scfg.ctrl_dir); /* legacy */
4764: semfile_list_add(&recycle_semfiles,path);
4765: if(!initialized) {
4766: initialized=time(NULL);
4767: semfile_list_check(&initialized,recycle_semfiles);
4768: semfile_list_check(&initialized,shutdown_semfiles);
4769: }
4770:
4771:
4772: /* signal caller that we've started up successfully */
4773: if(startup->started!=NULL)
4774: startup->started(startup->cbdata);
4775:
4776: while(server_socket!=INVALID_SOCKET && !terminate_server) {
4777:
4778: if(active_clients==0) {
4779: if(!(startup->options&FTP_OPT_NO_RECYCLE)) {
4780: if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) {
4781: lprintf(LOG_INFO,"0000 Recycle semaphore file (%s) detected",p);
4782: break;
4783: }
4784: #if 0 /* unused */
4785: if(startup->recycle_sem!=NULL && sem_trywait(&startup->recycle_sem)==0)
4786: startup->recycle_now=TRUE;
4787: #endif
4788: if(startup->recycle_now==TRUE) {
4789: lprintf(LOG_NOTICE,"0000 Recycle semaphore signaled");
4790: startup->recycle_now=FALSE;
4791: break;
4792: }
4793: }
4794: if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL
4795: && lprintf(LOG_INFO,"0000 Shutdown semaphore file (%s) detected",p))
4796: || (startup->shutdown_now==TRUE
4797: && lprintf(LOG_INFO,"0000 Shutdown semaphore signaled"))) {
4798: startup->shutdown_now=FALSE;
4799: terminate_server=TRUE;
4800: break;
4801: }
4802: }
4803: /* now wait for connection */
4804:
4805: tv.tv_sec=startup->sem_chk_freq;
4806: tv.tv_usec=0;
4807:
4808: FD_ZERO(&socket_set);
4809: FD_SET(server_socket,&socket_set);
4810:
4811: if((i=select(server_socket+1,&socket_set,NULL,NULL,&tv))<1) {
4812: if(i==0)
4813: continue;
4814: if(ERROR_VALUE==EINTR)
4815: lprintf(LOG_DEBUG,"%04d FTP Server listening interrupted", server_socket);
4816: else if(ERROR_VALUE == ENOTSOCK)
4817: lprintf(LOG_NOTICE,"%04d FTP Server socket closed", server_socket);
4818: else
4819: lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket",server_socket, ERROR_VALUE);
4820: continue;
4821: }
4822:
4823: if(server_socket==INVALID_SOCKET || terminate_server) /* terminated */
4824: break;
4825:
4826: client_addr_len = sizeof(client_addr);
4827: client_socket = accept(server_socket, (struct sockaddr *)&client_addr
4828: ,&client_addr_len);
4829:
4830: if(client_socket == INVALID_SOCKET)
4831: {
4832: #if 0 /* is this necessary still? */
4833: if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINTR || ERROR_VALUE == EINVAL) {
4834: lprintf(LOG_NOTICE,"0000 FTP socket closed while listening");
4835: break;
4836: }
4837: #endif
4838: lprintf(LOG_WARNING,"%04d !ERROR %d accepting connection"
4839: ,server_socket, ERROR_VALUE);
4840: #ifdef _WIN32
4841: if(WSAGetLastError()==WSAENOBUFS) /* recycle (re-init WinSock) on this error */
4842: break;
4843: #endif
4844: continue;
4845: }
4846: if(startup->socket_open!=NULL)
4847: startup->socket_open(startup->cbdata,TRUE);
4848: sockets++;
4849:
4850: if(trashcan(&scfg,inet_ntoa(client_addr.sin_addr),"ip-silent")) {
4851: ftp_close_socket(&client_socket,__LINE__);
4852: continue;
4853: }
4854:
4855: if(active_clients>=startup->max_clients) {
4856: lprintf(LOG_WARNING,"%04d !MAXIMUM CLIENTS (%d) reached, access denied"
4857: ,client_socket, startup->max_clients);
4858: sockprintf(client_socket,"421 Maximum active clients reached, please try again later.");
4859: mswait(3000);
4860: ftp_close_socket(&client_socket,__LINE__);
4861: continue;
4862: }
4863:
4864: if((ftp=malloc(sizeof(ftp_t)))==NULL) {
4865: lprintf(LOG_CRIT,"%04d !ERROR allocating %d bytes of memory for ftp_t"
4866: ,client_socket,sizeof(ftp_t));
4867: sockprintf(client_socket,"421 System error, please try again later.");
4868: mswait(3000);
4869: ftp_close_socket(&client_socket,__LINE__);
4870: continue;
4871: }
4872:
4873: ftp->socket=client_socket;
4874: ftp->client_addr=client_addr;
4875:
4876: _beginthread (ctrl_thread, 0, ftp);
4877: served++;
4878: }
4879:
4880: #ifdef _DEBUG
4881: lprintf(LOG_DEBUG,"0000 server_socket: %d",server_socket);
4882: lprintf(LOG_DEBUG,"0000 terminate_server: %d",terminate_server);
4883: #endif
4884: if(active_clients) {
4885: lprintf(LOG_DEBUG,"%04d Waiting for %d active clients to disconnect..."
4886: ,server_socket, active_clients);
4887: start=time(NULL);
4888: while(active_clients) {
4889: if(time(NULL)-start>startup->max_inactivity) {
4890: lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for %d active clients"
4891: ,server_socket, active_clients);
4892: break;
4893: }
4894: mswait(100);
4895: }
4896: }
4897:
4898: if(thread_count>1) {
4899: lprintf(LOG_DEBUG,"%04d Waiting for %d threads to terminate..."
4900: ,server_socket, thread_count-1);
4901: start=time(NULL);
4902: while(thread_count>1) {
4903: if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
4904: lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for %d threads"
4905: ,server_socket, thread_count-1);
4906: break;
4907: }
4908: mswait(100);
4909: }
4910: }
4911:
4912: cleanup(0,__LINE__);
4913:
4914: if(!terminate_server) {
4915: lprintf(LOG_INFO,"Recycling server...");
4916: mswait(2000);
4917: if(startup->recycle!=NULL)
4918: startup->recycle(startup->cbdata);
4919: }
4920:
4921: } while(!terminate_server);
4922: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.