|
|
1.1 root 1: /* ftpsrvr.c */
2:
3: /* Synchronet FTP server */
4:
5: /* $Id: ftpsrvr.c,v 1.52 2000/12/14 12:40:07 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 2000 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: /* Platform-specific headers */
39: #ifdef _WIN32
40:
41: #include <share.h> /* SH_DENYNO */
42: #include <direct.h> /* _mkdir/_rmdir() */
43: #include <process.h> /* _beginthread */
44: #include <windows.h> /* required for mmsystem.h */
45: #include <mmsystem.h> /* SND_ASYNC */
46:
47: #elif defined(__unix__)
48:
49: #include <signal.h> /* signal/SIGPIPE */
50:
51: #endif
52:
53:
54: /* ANSI C Library headers */
55: #include <stdio.h>
56: #include <stdlib.h> /* ltoa in GNU C lib */
57: #include <stdarg.h> /* va_list, varargs */
58: #include <string.h> /* strrchr */
59: #include <fcntl.h> /* O_WRONLY, O_RDONLY, etc. */
60: #include <errno.h> /* EACCES */
61: #include <ctype.h> /* toupper */
62: #include <sys/stat.h> /* S_IWRITE */
63:
64: /* Synchronet-specific headers */
65: #include "sbbs.h"
66: #include "ftpsrvr.h"
67: #include "telnet.h"
68:
69: /* Constants */
70:
71: #define FTP_VERSION "1.02"
72:
73: #define ILLEGAL_FILENAME_CHARS "\\/|<>+[]:=\";,%"
74:
75: #define STATUS_WFC "Listening"
76:
77: #define BBS_VIRTUAL_PATH "bbs:/""/" /* this is actually bbs:<slash><slash> */
78: #define LOCAL_FSYS_DIR "local:"
79: #define BBS_FSYS_DIR "bbs:"
80:
81: #define TIMEOUT_THREAD_WAIT 60 /* Seconds */
82:
83: #define XFER_REPORT_INTERVAL 60 /* Seconds */
84:
85: #define INDEX_FNAME_LEN 15
86:
87: #define NAME_LEN 15 /* User name length for listings */
88:
89: static const char *mon[]={"Jan","Feb","Mar","Apr","May","Jun"
90: ,"Jul","Aug","Sep","Oct","Nov","Dec"};
91:
92: BOOL direxist(char *dir)
93: {
94: if(access(dir,0)==0)
95: return(TRUE);
96: else
97: return(FALSE);
98: }
99:
100: BOOL dir_op(scfg_t* cfg, user_t* user, uint dirnum)
101: {
102: return(user->level>=SYSOP_LEVEL || user->exempt&FLAG('R')
103: || (cfg->dir[dirnum]->op_ar[0] && chk_ar(cfg,cfg->dir[dirnum]->op_ar,user)));
104: }
105:
106: static ftp_startup_t* startup=NULL;
107: static scfg_t scfg;
108: static SOCKET server_socket=INVALID_SOCKET;
109: static DWORD active_clients=0;
110: static DWORD sockets=0;
111: static HANDLE socket_mutex=NULL;
112:
113: typedef struct {
114: SOCKET socket;
115: SOCKADDR_IN client_addr;
116: } ftp_t;
117:
118: static int lprintf(char *fmt, ...)
119: {
120: int result;
121: va_list argptr;
122: char sbuf[1024];
123:
124: if(startup!=NULL && startup->lputs==NULL)
125: return(0);
126: va_start(argptr,fmt);
127: vsprintf(sbuf,fmt,argptr);
128: va_end(argptr);
129: result=startup->lputs(sbuf);
130:
131: return(result);
132: }
133:
134: #ifdef _WINSOCKAPI_
135:
136: static WSADATA WSAData;
137: static BOOL WSAInitialized=FALSE;
138:
139: static BOOL winsock_startup(void)
140: {
141: int status; /* Status Code */
142:
143: if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
144: lprintf("%s %s",WSAData.szDescription, WSAData.szSystemStatus);
145: WSAInitialized=TRUE;
146: return (TRUE);
147: }
148:
149: lprintf("!WinSock startup ERROR %d", status);
150: return (FALSE);
151: }
152:
153: #else /* No WINSOCK */
154:
155: #define winsock_startup() (TRUE)
156:
157: #endif
158:
159: static void status(char* str)
160: {
161: if(startup!=NULL && startup->status!=NULL)
162: startup->status(str);
163: }
164:
165: static void update_clients(void)
166: {
167: if(startup!=NULL && startup->clients!=NULL)
168: startup->clients(active_clients);
169: }
170:
171: static void client_on(SOCKET sock, client_t* client)
172: {
173: if(startup!=NULL && startup->client_on!=NULL)
174: startup->client_on(TRUE,sock,client);
175: }
176:
177: static void client_off(SOCKET sock)
178: {
179: if(startup!=NULL && startup->client_on!=NULL)
180: startup->client_on(FALSE,sock,NULL);
181: }
182:
183: static void thread_up(void)
184: {
185: if(startup!=NULL && startup->thread_up!=NULL)
186: startup->thread_up(TRUE);
187: }
188:
189: static void thread_down(void)
190: {
191: if(startup!=NULL && startup->thread_up!=NULL)
192: startup->thread_up(FALSE);
193: }
194:
195: static SOCKET open_socket(int type)
196: {
197: SOCKET sock;
198:
199: sock=socket(AF_INET, type, IPPROTO_IP);
200: if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
201: startup->socket_open(TRUE);
202: if(sock!=INVALID_SOCKET) {
203: sockets++;
204: #ifdef _DEBUG
205: lprintf("%04d Socket opened (%d sockets in use)",sock,sockets);
206: #endif
207: }
208: return(sock);
209: }
210:
211: #ifdef __BORLANDC__
212: #pragma argsused
213: #endif
214: static int close_socket(SOCKET* sock, int line)
215: {
216: int result;
217:
218: #ifndef _WIN32
219: #define ReleaseMutex(x)
220: #else
221: if((result=WaitForSingleObject(socket_mutex,5000))!=WAIT_OBJECT_0)
222: lprintf("%04d !ERROR %d getting socket mutex from line %d"
223: ,*sock,ERROR_VALUE,line);
224:
225: if(IsBadWritePtr(sock,sizeof(SOCKET))) {
226: ReleaseMutex(socket_mutex);
227: lprintf("0000 !BAD socket pointer in close_socket from line %d",line);
228: return(-1);
229: }
230: #endif
231: if((*sock)==INVALID_SOCKET) {
232: ReleaseMutex(socket_mutex);
233: lprintf("0000 !INVALID_SOCKET in close_socket from line %d",line);
234: return(-1);
235: }
236:
237: shutdown(*sock,SHUT_RDWR); /* required on Unix */
238:
239: result=closesocket(*sock);
240: if(/* result==0 && */ startup!=NULL && startup->socket_open!=NULL)
241: startup->socket_open(FALSE);
242:
243: sockets--;
244:
245: if(result!=0)
246: lprintf("%04d !ERROR %d closing socket from line %d",*sock,ERROR_VALUE,line);
247: else {
248: #ifdef _DEBUG
249: lprintf("%04d Socket closed (%d sockets in use) from line %d",*sock,sockets,line);
250: #endif
251: }
252: *sock=INVALID_SOCKET;
253: ReleaseMutex(socket_mutex);
254:
255: return(result);
256: }
257:
258: static int sockprintf(SOCKET sock, char *fmt, ...)
259: {
260: int len;
261: int result;
262: va_list argptr;
263: char sbuf[1024];
264:
265: va_start(argptr,fmt);
266: len=vsprintf(sbuf,fmt,argptr);
267: if(startup->options&FTP_OPT_DEBUG_TX)
268: lprintf("%04d TX: %s", sock, sbuf);
269: strcat(sbuf,"\r\n");
270: len+=2;
271: va_end(argptr);
272: while((result=send(sock,sbuf,len,0))!=len) {
273: if(result==SOCKET_ERROR) {
274: if(ERROR_VALUE==EWOULDBLOCK) {
275: mswait(1);
276: continue;
277: }
278: if(ERROR_VALUE==ECONNRESET)
279: lprintf("%04d Connection reset by peer on send",sock);
280: else if(ERROR_VALUE==ECONNABORTED)
281: lprintf("%04d Connection aborted by peer on send",sock);
282: else
283: lprintf("%04d !ERROR %d sending",sock,ERROR_VALUE);
284: return(0);
285: }
286: lprintf("%04d !ERROR: short send: %d instead of %d",sock,result,len);
287: }
288: return(len);
289: }
290:
291: /************************************************/
292: /* Truncates white-space chars off end of 'str' */
293: /************************************************/
294: static void truncsp(char *str)
295: {
296: uint c;
297:
298: c=strlen(str);
299: while(c && (uchar)str[c-1]<=' ') c--;
300: str[c]=0;
301: }
302:
303:
304: static int nopen(char *str, int access)
305: {
306: int file,share,count=0;
307:
308: if(access&O_DENYNONE) {
309: share=SH_DENYNO;
310: access&=~O_DENYNONE; }
311: else if(access==O_RDONLY) share=SH_DENYWR;
312: else share=SH_DENYRW;
313: while(((file=sopen(str,O_BINARY|access,share))==-1)
314: && errno==EACCES && count++<LOOP_NOPEN)
315: if(count>10)
316: mswait(55);
317: return(file);
318: }
319:
320: time_t gettimeleft(scfg_t* cfg, user_t* user, time_t starttime)
321: {
322: time_t now;
323: long tleft;
324: time_t timeleft;
325:
326: now=time(NULL);
327:
328: if(user->exempt&FLAG('T')) { /* Time online exemption */
329: timeleft=cfg->level_timepercall[user->level]*60;
330: if(timeleft<10) /* never get below 10 for exempt users */
331: timeleft=10; }
332: else {
333: tleft=(((long)cfg->level_timeperday[user->level]-user->ttoday)
334: +user->textra)*60L;
335: if(tleft<0) tleft=0;
336: if(tleft>cfg->level_timepercall[user->level]*60)
337: tleft=cfg->level_timepercall[user->level]*60;
338: tleft+=user->min*60L;
339: tleft-=now-starttime;
340: if(tleft>0x7fffL)
341: timeleft=0x7fff;
342: else
343: timeleft=tleft; }
344:
345: return(timeleft);
346: }
347:
348: static time_t checktime(void)
349: {
350: struct tm tm;
351:
352: memset(&tm,0,sizeof(tm));
353: tm.tm_year=94;
354: tm.tm_mday=1;
355: return(mktime(&tm)-0x2D24BD00L);
356: }
357:
358: BOOL upload_stats(ulong bytes)
359: {
360: char str[MAX_PATH];
361: int file;
362: ulong val;
363:
364: sprintf(str,"%sdsts.dab",scfg.ctrl_dir);
365: if((file=nopen(str,O_RDWR))==-1)
366: return(FALSE);
367:
368: lseek(file,20L,SEEK_SET); /* Skip timestamp, logons and logons today */
369: read(file,&val,4); /* Uploads today */
370: val++;
371: lseek(file,-4L,SEEK_CUR);
372: write(file,&val,4);
373: read(file,&val,4); /* Upload bytes today */
374: val+=bytes;
375: lseek(file,-4L,SEEK_CUR);
376: write(file,&val,4);
377: close(file);
378: return(TRUE);
379: }
380:
381: BOOL download_stats(ulong bytes)
382: {
383: char str[MAX_PATH];
384: int file;
385: ulong val;
386:
387: sprintf(str,"%sdsts.dab",scfg.ctrl_dir);
388: if((file=nopen(str,O_RDWR))==-1)
389: return(FALSE);
390:
391: lseek(file,28L,SEEK_SET); /* Skip timestamp, logons and logons today */
392: read(file,&val,4); /* Downloads today */
393: val++;
394: lseek(file,-4L,SEEK_CUR);
395: write(file,&val,4);
396: read(file,&val,4); /* Download bytes today */
397: val+=bytes;
398: lseek(file,-4L,SEEK_CUR);
399: write(file,&val,4);
400: close(file);
401: return(TRUE);
402: }
403:
404: void recverror(SOCKET socket, int rd)
405: {
406: if(rd==0)
407: lprintf("%04d Socket closed by peer on receive",socket);
408: else if(rd==SOCKET_ERROR) {
409: if(ERROR_VALUE==ECONNRESET)
410: lprintf("%04d Connection reset by peer on receive",socket);
411: else if(ERROR_VALUE==ECONNABORTED)
412: lprintf("%04d Connection aborted by peer on receive",socket);
413: else
414: lprintf("%04d !ERROR %d receiving on socket", socket, ERROR_VALUE);
415: } else
416: lprintf("%04d !ERROR: recv on socket returned unexpected value: %d",socket,rd);
417: }
418:
419: int sockreadline(SOCKET socket, char* buf, int len, time_t* lastactive)
420: {
421: char ch;
422: int i,rd=0;
423: fd_set socket_set;
424: struct timeval tv;
425:
426: while(rd<len-1) {
427:
428: tv.tv_sec=1;
429: tv.tv_usec=0;
430:
431: FD_ZERO(&socket_set);
432: FD_SET(socket,&socket_set);
433:
434: i=select(socket+1,&socket_set,NULL,NULL,&tv);
435:
436: if(server_socket==INVALID_SOCKET) {
437: sockprintf(socket,"421 Server downed, aborting.");
438: lprintf("%04d Server downed, aborting.",socket);
439: return(0);
440: }
441: if(i<1) {
442: if(i==0) {
443: if((time(NULL)-(*lastactive))>startup->max_inactivity) {
444: lprintf("%04d Disconnecting due to to inactivity.",socket);
445: sockprintf(socket,"421 Disconnecting due to inactivity (%u seconds)."
446: ,startup->max_inactivity);
447: return(0);
448: }
449: mswait(1);
450: continue;
451: }
452: recverror(socket,i);
453: return(i);
454: }
455: i=recv(socket, &ch, 1, 0);
456: if(i<1) {
457: #if 0
458: if(ERROR_VALUE==EWOULDBLOCK) {
459: mswait(1);
460: continue;
461: }
462: #endif
463: recverror(socket,i);
464: return(i);
465: }
466: if(ch=='\n' && rd>=1) {
467: break;
468: }
469: buf[rd++]=ch;
470: }
471: buf[rd-1]=0;
472:
473: return(rd);
474: }
475:
476: void DLLCALL ftp_terminate(void)
477: {
478: if(server_socket!=INVALID_SOCKET) {
479: lprintf("%04d FTP Terminate: closing socket",server_socket);
480: close_socket(&server_socket,__LINE__);
481: server_socket=INVALID_SOCKET;
482: }
483: }
484:
485:
486: typedef struct {
487: SOCKET ctrl_sock;
488: SOCKET* data_sock;
489: BOOL* inprogress;
490: BOOL* aborted;
491: BOOL delfile;
492: BOOL tmpfile;
493: BOOL credits;
494: BOOL append;
495: long filepos;
496: char filename[MAX_PATH];
497: time_t* lastactive;
498: user_t* user;
499: int dir;
500: char* desc;
501: } xfer_t;
502:
503: static void send_thread(void* arg)
504: {
505: char buf[8192];
506: char fname[MAX_PATH];
507: int i;
508: int rd;
509: int wr;
510: ulong total=0;
511: ulong dur;
512: ulong cps;
513: ulong length;
514: BOOL error=FALSE;
515: FILE* fp;
516: file_t f;
517: xfer_t xfer;
518: time_t now;
519: time_t start;
520: time_t last_report;
521:
522: xfer=*(xfer_t*)arg;
523:
524: length=flength(xfer.filename);
525:
526: if((fp=fopen(xfer.filename,"rb"))==NULL) {
527: lprintf("%04d !DATA ERROR %d opening %s",xfer.ctrl_sock,errno,xfer.filename);
528: sockprintf(xfer.ctrl_sock,"450 ERROR %d opening %s.",errno,xfer.filename);
529: if(xfer.tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES))
530: remove(xfer.filename);
531: close_socket(xfer.data_sock,__LINE__);
532: *xfer.inprogress=FALSE;
533: return;
534: }
535: thread_up();
536:
537: *xfer.inprogress=TRUE;
538: *xfer.aborted=FALSE;
539: if(startup->options&FTP_OPT_DEBUG_DATA || xfer.filepos)
540: lprintf("%04d DATA socket %d sending %s from offset %ld"
541: ,xfer.ctrl_sock,*xfer.data_sock,xfer.filename,xfer.filepos);
542:
543: fseek(fp,xfer.filepos,SEEK_SET);
544: last_report=start=time(NULL);
545: while(!feof(fp)) {
546: now=time(NULL);
547: if(total && now>=last_report+XFER_REPORT_INTERVAL) {
548: lprintf("%04d Sent %ld bytes (%ld total) of %s (%lu cps)"
549: ,xfer.ctrl_sock,total,length,xfer.filename
550: ,now-start ? total/(now-start) : total*2);
551: last_report=now;
552: }
553:
554: if(*xfer.aborted==TRUE) {
555: lprintf("%04d !DATA Transfer aborted",xfer.ctrl_sock);
556: sockprintf(xfer.ctrl_sock,"426 Transfer aborted.");
557: error=TRUE;
558: break;
559: }
560: if(server_socket==INVALID_SOCKET) {
561: lprintf("%04d !DATA Transfer locally aborted",xfer.ctrl_sock);
562: sockprintf(xfer.ctrl_sock,"426 Transfer locally aborted.");
563: error=TRUE;
564: break;
565: }
566: rd=fread(buf,sizeof(char),sizeof(buf),fp);
567: if(rd<1) {
568: lprintf("%04d !READ ERROR (%d) on %s",xfer.ctrl_sock,errno,xfer.filename);
569: error=TRUE;
570: break;
571: }
572: wr=send(*xfer.data_sock,buf,rd,0);
573: if(wr!=rd) {
574: if(wr==SOCKET_ERROR) {
575: if(ERROR_VALUE==ECONNRESET)
576: lprintf("%04d DATA Connection reset by peer, sending on socket %d"
577: ,xfer.ctrl_sock,*xfer.data_sock);
578: else if(ERROR_VALUE==ECONNABORTED)
579: lprintf("%04d DATA Connection aborted by peer, sending on socket %d"
580: ,xfer.ctrl_sock,*xfer.data_sock);
581: else
582: lprintf("%04d !DATA ERROR %d sending on data socket %d"
583: ,xfer.ctrl_sock,ERROR_VALUE,*xfer.data_sock);
584: sockprintf(xfer.ctrl_sock,"426 Error %d sending on DATA channel",ERROR_VALUE);
585: error=TRUE;
586: break;
587: }
588: if(wr==0) {
589: lprintf("%04d !DATA socket %d disconnected",xfer.ctrl_sock, *xfer.data_sock);
590: sockprintf(xfer.ctrl_sock,"426 DATA channel disconnected");
591: error=TRUE;
592: break;
593: }
594: lprintf("%04d !DATA ERROR sent %d instead of %d on socket %d"
595: ,xfer.ctrl_sock,wr,rd,*xfer.data_sock);
596: sockprintf(xfer.ctrl_sock,"451 Short DATA transfer");
597: error=TRUE;
598: break;
599: }
600: total+=wr;
601: *xfer.lastactive=time(NULL);
602: mswait(1);
603: }
604:
605: if((i=ferror(fp))!=0)
606: lprintf("%04d !FILE ERROR %d (%d)",xfer.ctrl_sock,i,errno);
607:
608: close_socket(xfer.data_sock,__LINE__); /* Signal end of file */
609: if(startup->options&FTP_OPT_DEBUG_DATA)
610: lprintf("%04d DATA socket closed",xfer.ctrl_sock);
611:
612: if(!error) {
613: dur=time(NULL)-start;
614: cps=dur ? total/dur : total*2;
615: lprintf("%04d Transfer successful: %lu bytes sent in %lu seconds (%lu cps)"
616: ,xfer.ctrl_sock
617: ,total,dur,cps);
618: sockprintf(xfer.ctrl_sock,"226 Download complete (%lu cps).",cps);
619:
620: if(xfer.dir>=0) {
621: memset(&f,0,sizeof(f));
622: #ifdef _WIN32
623: GetShortPathName(xfer.filename,fname,sizeof(fname));
624: #else
625: strcpy(xfer.filename,fname);
626: #endif
627: padfname(getfname(fname),f.name);
628: strupr(f.name);
629: f.dir=xfer.dir;
630: f.size=total;
631: if(getfileixb(&scfg,&f)==TRUE && getfiledat(&scfg,&f)==TRUE) {
632: f.timesdled++;
633: putfiledat(&scfg,&f);
634: lprintf("%04d %s downloaded: %s (%lu times total)"
635: ,xfer.ctrl_sock
636: ,xfer.user->alias
637: ,xfer.filename
638: ,f.timesdled);
639: }
640: /* Need to update datedled in index */
641: }
642:
643: if(xfer.credits) {
644: xfer.user->dls=(ushort)adjustuserrec(&scfg, xfer.user->number,U_DLS,5,1);
645: xfer.user->dlb=adjustuserrec(&scfg, xfer.user->number,U_DLB,10,total);
646: if(xfer.dir>=0 && !(scfg.dir[xfer.dir]->misc&DIR_FREE)
647: && !(xfer.user->exempt&FLAG('D')))
648: subtract_cdt(&scfg, xfer.user, xfer.credits);
649: }
650: if(!xfer.tmpfile && !xfer.delfile)
651: download_stats(total);
652: }
653:
654: fclose(fp);
655: *xfer.inprogress=FALSE;
656: if(xfer.tmpfile) {
657: if(!(startup->options&FTP_OPT_KEEP_TEMP_FILES))
658: remove(xfer.filename);
659: }
660: else if(xfer.delfile && !error)
661: remove(xfer.filename);
662:
663: thread_down();
664: }
665:
666: static void receive_thread(void* arg)
667: {
668: char buf[8192];
669: char fname[MAX_PATH];
670: int rd;
671: int file;
672: ulong total=0;
673: ulong dur;
674: ulong cps;
675: BOOL error=FALSE;
676: FILE* fp;
677: file_t f;
678: xfer_t xfer;
679: time_t now;
680: time_t start;
681: time_t last_report;
682:
683: xfer=*(xfer_t*)arg;
684:
685: if((fp=fopen(xfer.filename,xfer.append ? "ab" : "wb"))==NULL) {
686: lprintf("%04d !DATA ERROR %d opening %s",xfer.ctrl_sock,xfer.filename,errno);
687: close_socket(xfer.data_sock,__LINE__);
688: return;
689: }
690:
691: thread_up();
692:
693: *xfer.inprogress=TRUE;
694: *xfer.aborted=FALSE;
695: if(xfer.filepos || startup->options&FTP_OPT_DEBUG_DATA)
696: lprintf("%04d DATA socket %d receiving from offset %ld"
697: ,xfer.ctrl_sock,*xfer.data_sock,xfer.filepos);
698:
699: fseek(fp,xfer.filepos,SEEK_SET);
700: last_report=start=time(NULL);
701: while(1) {
702:
703: now=time(NULL);
704: if(total && now>=last_report+XFER_REPORT_INTERVAL) {
705: lprintf("%04d Received %ld bytes of %s (%lu cps)"
706: ,xfer.ctrl_sock,total,xfer.filename
707: ,now-start? total/(now-start) : total*2);
708: last_report=now;
709: }
710: if(*xfer.aborted==TRUE) {
711: lprintf("%04d !DATA Transfer aborted",xfer.ctrl_sock);
712: /* Send NAK */
713: sockprintf(xfer.ctrl_sock,"426 Transfer aborted.");
714: error=TRUE;
715: break;
716: }
717: if(server_socket==INVALID_SOCKET) {
718: lprintf("%04d !DATA Transfer locally aborted",xfer.ctrl_sock);
719: /* Send NAK */
720: sockprintf(xfer.ctrl_sock,"426 Transfer locally aborted.");
721: error=TRUE;
722: break;
723: }
724: rd=recv(*xfer.data_sock,buf,sizeof(buf),0);
725: if(rd<1) {
726: if(rd==0) { /* Socket closed */
727: if(startup->options&FTP_OPT_DEBUG_DATA)
728: lprintf("%04d DATA socket %d closed by client"
729: ,xfer.ctrl_sock,*xfer.data_sock);
730: break;
731: }
732: if(rd==SOCKET_ERROR) {
733: if(ERROR_VALUE==ECONNRESET)
734: lprintf("%04d Connection reset by peer, receiving on socket %d"
735: ,xfer.ctrl_sock,*xfer.data_sock);
736: else if(ERROR_VALUE==ECONNABORTED)
737: lprintf("%04d Connection aborted by peer, receiving on socket %d"
738: ,xfer.ctrl_sock,*xfer.data_sock);
739: else
740: lprintf("%04d !DATA ERROR %d receiving on data socket %d"
741: ,xfer.ctrl_sock,ERROR_VALUE,*xfer.data_sock);
742: /* Send NAK */
743: sockprintf(xfer.ctrl_sock,"426 Error %d receiving on DATA channel"
744: ,ERROR_VALUE);
745: error=TRUE;
746: break;
747: }
748: lprintf("%04d !DATA ERROR recv returned %d on socket %d"
749: ,xfer.ctrl_sock,rd,*xfer.data_sock);
750: /* Send NAK */
751: sockprintf(xfer.ctrl_sock,"451 Unexpected socket error: %d",rd);
752: error=TRUE;
753: break;
754: }
755: fwrite(buf,1,rd,fp);
756: total+=rd;
757: *xfer.lastactive=time(NULL);
758: mswait(1);
759: }
760:
761: *xfer.inprogress=FALSE;
762: fclose(fp);
763:
764: close_socket(xfer.data_sock,__LINE__);
765: if(error && startup->options&FTP_OPT_DEBUG_DATA)
766: lprintf("%04d DATA socket %d closed",xfer.ctrl_sock,*xfer.data_sock);
767:
768: if(!error) {
769: dur=time(NULL)-start;
770: cps=dur ? total/dur : total*2;
771: lprintf("%04d Transfer successful: %lu bytes received in %lu seconds (%lu cps)"
772: ,xfer.ctrl_sock
773: ,total,dur,cps);
774:
775: if(xfer.dir>=0) {
776: memset(&f,0,sizeof(f));
777: #ifdef _WIN32
778: GetShortPathName(xfer.filename,fname,sizeof(fname));
779: #else
780: strcpy(fname,xfer.filename);
781: #endif
782: padfname(getfname(fname),f.name);
783: strupr(f.name);
784: f.dir=xfer.dir;
785: if(scfg.dir[f.dir]->misc&DIR_AONLY) /* Forced anonymous */
786: f.misc|=FM_ANON;
787: f.cdt=total;
788: f.dateuled=time(NULL);
789: f.timesdled=0;
790: f.datedled=0L;
791: f.opencount=0;
792: if(xfer.desc==NULL || *xfer.desc==0) {
793: sprintf(f.desc,"%.*s",(int)sizeof(f.desc)-1,getfname(xfer.filename));
794: /* old way strcpy(f.desc,"Received via FTP: No description given"); */
795: } else
796: sprintf(f.desc,"%.*s",(int)sizeof(f.desc)-1,xfer.desc);
797: strcpy(f.uler,xfer.user->alias);
798: if(!addfiledat(&scfg,&f))
799: lprintf("%04d !ERROR adding file (%s) to database",xfer.ctrl_sock,f.name);
800: if(scfg.dir[f.dir]->upload_sem[0])
801: if((file=sopen(scfg.dir[f.dir]->upload_sem,O_WRONLY|O_CREAT|O_TRUNC,SH_DENYNO))!=-1)
802: close(file);
803: /**************************/
804: /* Update Uploader's Info */
805: /**************************/
806: xfer.user->uls=(short)adjustuserrec(&scfg, xfer.user->number,U_ULS,5,1);
807: xfer.user->ulb=adjustuserrec(&scfg, xfer.user->number,U_ULB,10,total);
808: if(scfg.dir[f.dir]->up_pct && scfg.dir[f.dir]->misc&DIR_CDTUL) { /* credit for upload */
809: if(scfg.dir[f.dir]->misc&DIR_CDTMIN && cps) /* Give min instead of cdt */
810: xfer.user->min=adjustuserrec(&scfg,xfer.user->number,U_MIN,10
811: ,((ulong)(total*(scfg.dir[f.dir]->up_pct/100.0))/cps)/60);
812: else
813: xfer.user->cdt=adjustuserrec(&scfg,xfer.user->number,U_CDT,10
814: ,(ulong)(f.cdt*(scfg.dir[f.dir]->up_pct/100.0)));
815: }
816: upload_stats(total);
817: }
818: /* Send ACK */
819: sockprintf(xfer.ctrl_sock,"226 Upload complete (%lu cps).",cps);
820: }
821:
822: thread_down();
823: }
824:
825:
826:
827: static void filexfer(SOCKADDR_IN* addr, SOCKET ctrl_sock, SOCKET pasv_sock, SOCKET* data_sock
828: ,char* filename, long filepos, BOOL* inprogress, BOOL* aborted
829: ,BOOL delfile, BOOL tmpfile
830: ,time_t* lastactive
831: ,user_t* user
832: ,int dir
833: ,BOOL receiving
834: ,BOOL credits
835: ,BOOL append
836: ,char* desc)
837: {
838: int result;
839: int addr_len;
840: SOCKADDR_IN server_addr;
841: static xfer_t xfer;
842:
843: if((*inprogress)==TRUE) {
844: lprintf("%04d !TRANSFER already in progress",ctrl_sock);
845: sockprintf(ctrl_sock,"425 Transfer already in progress.");
846: return;
847: }
848: *inprogress=TRUE;
849:
850: if(*data_sock!=INVALID_SOCKET)
851: close_socket(data_sock,__LINE__);
852:
853: if(pasv_sock==INVALID_SOCKET) { /* !PASV */
854:
855: if((*data_sock=socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) == INVALID_SOCKET) {
856: lprintf("%04d !DATA ERROR %d opening socket", ctrl_sock, ERROR_VALUE);
857: sockprintf(ctrl_sock,"425 Error %d opening socket",ERROR_VALUE);
858: if(tmpfile)
859: remove(filename);
860: *inprogress=FALSE;
861: return;
862: }
863: if(startup->socket_open!=NULL)
864: startup->socket_open(TRUE);
865: sockets++;
866: if(startup->options&FTP_OPT_DEBUG_DATA)
867: lprintf("%04d DATA socket %d opened",ctrl_sock,*data_sock);
868:
869: memset(&server_addr, 0, sizeof(server_addr));
870:
871: server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
872: server_addr.sin_family = AF_INET;
873: server_addr.sin_port = htons(0);
874:
875: if((result=bind(*data_sock, (struct sockaddr *) &server_addr
876: ,sizeof(server_addr)))!=0) {
877: lprintf ("%04d !DATA ERROR %d (%d) binding socket %d"
878: ,ctrl_sock, result, ERROR_VALUE, *data_sock);
879: sockprintf(ctrl_sock,"425 Error %d binding socket",ERROR_VALUE);
880: if(tmpfile)
881: remove(filename);
882: *inprogress=FALSE;
883: close_socket(data_sock,__LINE__);
884: return;
885: }
886:
887: if((result=connect(*data_sock, (struct sockaddr *)addr
888: ,sizeof(struct sockaddr)))!=0) {
889: lprintf("%04d !DATA ERROR %d (%d) connecting to client %s port %d on socket %d"
890: ,ctrl_sock,result,ERROR_VALUE
891: ,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port),*data_sock);
892: sockprintf(ctrl_sock,"425 Error %d connecting to socket",ERROR_VALUE);
893: if(tmpfile)
894: remove(filename);
895: *inprogress=FALSE;
896: close_socket(data_sock,__LINE__);
897: return;
898: }
899: if(startup->options&FTP_OPT_DEBUG_DATA)
900: lprintf("%04d DATA socket %d connected to %s port %d"
901: ,ctrl_sock,*data_sock,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port));
902:
903: } else { /* PASV */
904: addr_len=sizeof(SOCKADDR_IN);
905: if((*data_sock=accept(pasv_sock,(struct sockaddr*)addr,&addr_len))==INVALID_SOCKET) {
906: lprintf("%04d !PASV DATA ERROR %d accepting connection on socket %d"
907: ,ctrl_sock,ERROR_VALUE,pasv_sock);
908: sockprintf(ctrl_sock,"425 Error %d accepting connection",ERROR_VALUE);
909: if(tmpfile)
910: remove(filename);
911: *inprogress=FALSE;
912: return;
913: }
914: if(startup->socket_open!=NULL)
915: startup->socket_open(TRUE);
916: sockets++;
917: if(startup->options&FTP_OPT_DEBUG_DATA)
918: lprintf("%04d PASV DATA socket %d connected to %s port %d"
919: ,ctrl_sock,*data_sock,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port));
920: }
921:
922:
923: /* Get Mutex here? */
924: memset(&xfer,0,sizeof(xfer));
925: xfer.ctrl_sock=ctrl_sock;
926: xfer.data_sock=data_sock;
927: xfer.inprogress=inprogress;
928: xfer.aborted=aborted;
929: xfer.delfile=delfile;
930: xfer.tmpfile=tmpfile;
931: xfer.append=append;
932: xfer.filepos=filepos;
933: xfer.credits=credits;
934: xfer.lastactive=lastactive;
935: xfer.user=user;
936: xfer.dir=dir;
937: xfer.desc=desc;
938: sprintf(xfer.filename,"%.*s",(int)sizeof(xfer.filename)-1,filename);
939: if(receiving)
940: _beginthread(receive_thread,0,(void*)&xfer);
941: else
942: _beginthread(send_thread,0,(void*)&xfer);
943: }
944:
945: /* convert "user name" to "user.name" or "mr. user" to "mr._user" */
946: char* dotname(char* in, char* out)
947: {
948: char ch;
949: int i;
950:
951: if(strchr(in,'.')==NULL)
952: ch='.';
953: else
954: ch='_';
955: for(i=0;in[i];i++)
956: if(in[i]<=' ')
957: out[i]=ch;
958: else
959: out[i]=in[i];
960: out[i]=0;
961: return(out);
962: }
963:
964: /* Returns the directory index of a virtual lib/dir path (e.g. main/games/filename) */
965: int getdir(char* p, user_t* user)
966: {
967: char* tp;
968: char path[MAX_PATH];
969: int dir;
970: int lib;
971:
972: sprintf(path,"%.*s",(int)sizeof(path)-1,p);
973: p=path;
974:
975: if(*p=='/')
976: p++;
977: else if(!strncmp(p,"./",2))
978: p+=2;
979:
980: tp=strchr(p,'/');
981: if(tp) *tp=0;
982: for(lib=0;lib<scfg.total_libs;lib++) {
983: if(!chk_ar(&scfg,scfg.lib[lib]->ar,user))
984: continue;
985: if(!stricmp(scfg.lib[lib]->sname,p))
986: break;
987: }
988: if(lib>=scfg.total_libs)
989: return(-1);
990:
991: if(tp!=NULL)
992: p=tp+1;
993:
994: tp=strchr(p,'/');
995: if(tp) *tp=0;
996: for(dir=0;dir<scfg.total_dirs;dir++) {
997: if(scfg.dir[dir]->lib!=lib)
998: continue;
999: if(dir!=scfg.sysop_dir && dir!=scfg.upload_dir
1000: && !chk_ar(&scfg,scfg.dir[dir]->ar,user))
1001: continue;
1002: if(!stricmp(scfg.dir[dir]->code,p))
1003: break;
1004: }
1005: if(dir>=scfg.total_dirs)
1006: return(-1);
1007:
1008: return(dir);
1009: }
1010:
1011: void parsepath(char** pp, user_t* user, int* curlib, int* curdir)
1012: {
1013: char* p;
1014: char* tp;
1015: char path[MAX_PATH];
1016: int dir=*curdir;
1017: int lib=*curlib;
1018:
1019: sprintf(path,"%.*s",(int)sizeof(path)-1,*pp);
1020: p=path;
1021:
1022: if(*p=='/') {
1023: p++;
1024: lib=-1;
1025: }
1026: else if(!strncmp(p,"./",2))
1027: p+=2;
1028:
1029: if(!strncmp(p,"..",2)) {
1030: p+=2;
1031: if(dir>=0)
1032: dir=-1;
1033: else if(lib>=0)
1034: lib=-1;
1035: if(*p=='/')
1036: p++;
1037: }
1038:
1039: if(*p==0) {
1040: *curlib=lib;
1041: *curdir=dir;
1042: return;
1043: }
1044:
1045: if(lib<0) {
1046: tp=strchr(p,'/');
1047: if(tp) *tp=0;
1048: for(lib=0;lib<scfg.total_libs;lib++) {
1049: if(!chk_ar(&scfg,scfg.lib[lib]->ar,user))
1050: continue;
1051: if(!stricmp(scfg.lib[lib]->sname,p))
1052: break;
1053: }
1054: if(lib>=scfg.total_libs) { /* not found */
1055: *curlib=-1;
1056: return;
1057: }
1058: *curlib=lib;
1059:
1060: if(tp==NULL) {
1061: *curdir=-1;
1062: return;
1063: }
1064:
1065: p=tp+1;
1066: }
1067:
1068: tp=strchr(p,'/');
1069: if(tp!=NULL) {
1070: *tp=0;
1071: tp++;
1072: } else
1073: tp=p+strlen(p);
1074:
1075: for(dir=0;dir<scfg.total_dirs;dir++) {
1076: if(scfg.dir[dir]->lib!=lib)
1077: continue;
1078: if(!chk_ar(&scfg,scfg.dir[dir]->ar,user))
1079: continue;
1080: if(!stricmp(scfg.dir[dir]->code,p))
1081: break;
1082: }
1083:
1084: if(dir>=scfg.total_dirs) /* not found */
1085: return;
1086:
1087: *curdir=dir;
1088:
1089: *pp+=tp-path; /* skip "lib/dir/" */
1090: }
1091:
1092: BOOL alias(char* fullalias, char* filename, user_t* user, int* curdir)
1093: {
1094: char* p;
1095: char* tp;
1096: char* fname="";
1097: char line[512];
1098: char alias[512];
1099: char aliasfile[MAX_PATH];
1100: int dir=-1;
1101: FILE* fp;
1102: BOOL result=FALSE;
1103:
1104: sprintf(aliasfile,"%sftpalias.cfg",scfg.ctrl_dir);
1105: if((fp=fopen(aliasfile,"r"))==NULL)
1106: return(result);
1107:
1108: sprintf(alias,"%.*s",(int)sizeof(alias)-1,fullalias);
1109: p=strrchr(alias+1,'/');
1110: if(p) {
1111: *p=0;
1112: fname=p+1;
1113: }
1114:
1115: while(!feof(fp)) {
1116: if(!fgets(line,sizeof(line)-1,fp))
1117: break;
1118:
1119: p=line; /* alias */
1120: while(*p && *p<=' ') p++;
1121: if(*p==';') /* comment */
1122: continue;
1123:
1124: tp=p; /* terminator */
1125: while(*tp && *tp>' ') tp++;
1126: if(*tp) *tp=0;
1127:
1128: if(stricmp(p,alias)) /* Not a match */
1129: continue;
1130:
1131: p=tp+1; /* filename */
1132: while(*p && *p<=' ') p++;
1133:
1134: tp=p; /* terminator */
1135: while(*tp && *tp>' ') tp++;
1136: if(*tp) *tp=0;
1137:
1138: if(!strnicmp(p,BBS_VIRTUAL_PATH,strlen(BBS_VIRTUAL_PATH))) {
1139: if((dir=getdir(p+strlen(BBS_VIRTUAL_PATH),user))<0) {
1140: lprintf("0000 !Invalid virtual path (%s) for %s",p,user->alias);
1141: /* invalid or no access */
1142: continue;
1143: }
1144: p=strrchr(p,'/');
1145: if(p!=NULL) p++;
1146: if(p!=NULL && filename!=NULL) {
1147: if(*p)
1148: sprintf(filename,"%s%s",scfg.dir[dir]->path,p);
1149: else
1150: sprintf(filename,"%s%s",scfg.dir[dir]->path,fname);
1151: }
1152: } else if(filename!=NULL)
1153: strcpy(filename,p);
1154:
1155: result=TRUE; /* success */
1156: break;
1157: }
1158: fclose(fp);
1159: if(curdir!=NULL)
1160: *curdir=dir;
1161: return(result);
1162: }
1163:
1164: char* root_dir(char* path)
1165: {
1166: char* p;
1167: static char root[MAX_PATH];
1168:
1169: sprintf(root,"%.*s",(int)sizeof(root)-1,path);
1170:
1171: if(!strncmp(root,"\\\\",2)) { /* network path */
1172: p=strchr(root+2,'\\');
1173: if(p) p=strchr(p+1,'\\');
1174: if(p) *(p+1)=0; /* truncate at \\computer\sharename\ */
1175: }
1176: else if(!strncmp(root+1,":/",2) || !strncmp(root+1,":\\",2))
1177: root[3]=0;
1178: else if(*root=='/' || *root=='\\')
1179: root[1]=0;
1180:
1181: return(root);
1182: }
1183:
1184: static void ctrl_thread(void* arg)
1185: {
1186: char buf[512];
1187: char str[128];
1188: char* cmd;
1189: char* p;
1190: char* np;
1191: char* tp;
1192: char password[64];
1193: char fname[MAX_PATH];
1194: char qwkfile[MAX_PATH];
1195: char aliasfile[MAX_PATH];
1196: char aliasline[512];
1197: char desc[501]="";
1198: char sys_pass[128];
1199: char* host_name;
1200: char host_ip[64];
1201: char path[MAX_PATH];
1202: char local_dir[MAX_PATH];
1203: char ren_from[MAX_PATH]="";
1204: WORD port;
1205: ulong ip_addr;
1206: int addr_len;
1207: DWORD h1,h2,h3,h4;
1208: u_short p1,p2; /* For PORT command */
1209: int i;
1210: int rd;
1211: int file;
1212: int result;
1213: int lib;
1214: int dir;
1215: int curlib=-1;
1216: int curdir=-1;
1217: int orglib;
1218: int orgdir;
1219: long filepos=0L;
1220: long timeleft;
1221: ulong l;
1222: ulong avail;
1223: BOOL detail;
1224: BOOL success;
1225: BOOL getdate;
1226: BOOL getsize;
1227: BOOL delecmd;
1228: BOOL delfile;
1229: BOOL tmpfile;
1230: BOOL credits;
1231: BOOL filedat;
1232: BOOL transfer_inprogress;
1233: BOOL transfer_aborted;
1234: BOOL sysop=FALSE;
1235: BOOL local_fsys=FALSE;
1236: BOOL alias_dir;
1237: FILE* fp;
1238: FILE* alias_fp;
1239: SOCKET sock;
1240: SOCKET pasv_sock=INVALID_SOCKET;
1241: SOCKET data_sock=INVALID_SOCKET;
1242: HOSTENT* host;
1243: SOCKADDR_IN addr;
1244: SOCKADDR_IN data_addr;
1245: SOCKADDR_IN pasv_addr;
1246: ftp_t ftp=*(ftp_t*)arg;
1247: user_t user;
1248: time_t t;
1249: time_t now;
1250: time_t logintime;
1251: time_t lastactive;
1252: file_t f;
1253: glob_t g;
1254: node_t node;
1255: client_t client;
1256: struct tm tm;
1257: struct tm * tm_p;
1258: struct tm cur_tm;
1259:
1260: thread_up();
1261:
1262: lastactive=time(NULL);
1263:
1264: sock=ftp.socket;
1265: data_addr=ftp.client_addr;
1266: /* Default data port is ctrl port-1 */
1267: data_addr.sin_port=ntohs(data_addr.sin_port)-1;
1268: data_addr.sin_port=htons(data_addr.sin_port);
1269:
1270: lprintf("%04d CTRL thread started", sock);
1271:
1272: free(arg);
1273:
1274: #ifdef _WIN32
1275: if(startup->answer_sound[0] && !(startup->options&FTP_OPT_MUTE))
1276: PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);
1277: #endif
1278:
1279: transfer_inprogress = FALSE;
1280: transfer_aborted = FALSE;
1281:
1282: l=1;
1283:
1284: if((i=ioctlsocket(sock, FIONBIO, &l))!=0) {
1285: lprintf("%04d !ERROR %d (%d) disabling socket blocking"
1286: ,sock, i, ERROR_VALUE);
1287: sockprintf(sock,"425 Error %d disabling socket blocking"
1288: ,ERROR_VALUE);
1289: close_socket(&sock,__LINE__);
1290: thread_down();
1291: return;
1292: }
1293:
1294: memset(&user,0,sizeof(user));
1295:
1296: strcpy(host_ip,inet_ntoa(ftp.client_addr.sin_addr));
1297:
1298: lprintf ("%04d CTRL connection accepted from: %s", sock, host_ip);
1299:
1300: if(startup->options&FTP_OPT_NO_HOST_LOOKUP)
1301: host=NULL;
1302: else
1303: host=gethostbyaddr ((char *)&ftp.client_addr.sin_addr
1304: ,sizeof(ftp.client_addr.sin_addr),AF_INET);
1305:
1306: if(host!=NULL && host->h_name!=NULL)
1307: host_name=host->h_name;
1308: else
1309: host_name="<no name>";
1310:
1311: lprintf("%04d Host name: %s", sock, host_name);
1312:
1313: if(trashcan(&scfg,host_ip,"ip")) {
1314: lprintf("%04d !Client blocked in ip.can: %s", sock, host_ip);
1315: sockprintf(sock,"550 Access denied.");
1316: close_socket(&sock,__LINE__);
1317: thread_down();
1318: return;
1319: }
1320:
1321: if(trashcan(&scfg,host_name,"host")) {
1322: lprintf("%04d !Client blocked in host.can: %s", sock, host_name);
1323: sockprintf(sock,"550 Access denied.");
1324: close_socket(&sock,__LINE__);
1325: thread_down();
1326: return;
1327: }
1328:
1329: /* For PASV mode */
1330: addr_len=sizeof(pasv_addr);
1331: if((result=getsockname(sock, (struct sockaddr *)&pasv_addr,&addr_len))!=0) {
1332: lprintf("%04d !ERROR %d (%d) getting address/port", sock, result, ERROR_VALUE);
1333: sockprintf(sock,"425 Error %d getting address/port",ERROR_VALUE);
1334: close_socket(&sock,__LINE__);
1335: thread_down();
1336: return;
1337: }
1338:
1339: active_clients++;
1340: update_clients();
1341:
1342: /* Initialize client display */
1343: client.size=sizeof(client);
1344: client.time=time(NULL);
1345: sprintf(client.addr,"%.*s",(int)sizeof(client.addr)-1,host_ip);
1346: sprintf(client.host,"%.*s",(int)sizeof(client.host)-1,host_name);
1347: client.port=ntohs(ftp.client_addr.sin_port);
1348: client.protocol="FTP";
1349: client.user="<unknown>";
1350: client_on(sock,&client);
1351:
1352: sockprintf(sock,"220-%s (%s)",scfg.sys_name, scfg.sys_inetaddr);
1353: sockprintf(sock," Synchronet FTP Server for %s v%s Ready"
1354: ,PLATFORM_DESC,FTP_VERSION);
1355: sprintf(str,"%sftplogin.txt",scfg.text_dir);
1356: if((fp=fopen(str,"rb"))!=NULL) {
1357: while(!feof(fp)) {
1358: if(!fgets(buf,sizeof(buf),fp))
1359: break;
1360: truncsp(buf);
1361: sockprintf(sock," %s",buf);
1362: }
1363: fclose(fp);
1364: }
1365: sockprintf(sock,"220 Please enter your user name.");
1366:
1367: while(1) {
1368:
1369: rd = sockreadline(sock, buf, sizeof(buf), &lastactive);
1370: if(rd<1) {
1371: if(transfer_inprogress==TRUE)
1372: transfer_aborted=TRUE;
1373: break;
1374: }
1375: lastactive=time(NULL);
1376: cmd=buf;
1377: while(((BYTE)*cmd)==TELNET_IAC) {
1378: cmd++;
1379: lprintf("%04d RX: Telnet cmd %d",sock,(BYTE)*cmd);
1380: cmd++;
1381: }
1382: while(*cmd && *cmd<' ') {
1383: lprintf("%04d RX: %d (0x%02X)",sock,(BYTE)*cmd,(BYTE)*cmd);
1384: cmd++;
1385: }
1386: if(!(*cmd))
1387: continue;
1388: if(startup->options&FTP_OPT_DEBUG_RX)
1389: lprintf("%04d RX: %s", sock, cmd);
1390: if(!stricmp(cmd, "NOOP")) {
1391: sockprintf(sock,"200 NOOP command successful.");
1392: continue;
1393: }
1394: if(!stricmp(cmd, "HELP SITE") || !stricmp(cmd, "SITE HELP")) {
1395: sockprintf(sock,"214-The following SITE commands are recognized (* => unimplemented):");
1396: sockprintf(sock," HELP WHO");
1397: sockprintf(sock,"214 Direct comments to sysop@%s.",scfg.sys_inetaddr);
1398: continue;
1399: }
1400: if(!strnicmp(cmd, "HELP",4)) {
1401: sockprintf(sock,"214-The following commands are recognized (* => unimplemented, # => extension):");
1402: sockprintf(sock," USER PASS CWD XCWD CDUP XCUP PWD XPWD");
1403: sockprintf(sock," QUIT REIN PORT PASV LIST NLST NOOP HELP");
1404: sockprintf(sock," SIZE MDTM RETR STOR REST ALLO ABOR SYST");
1405: sockprintf(sock," TYPE STRU MODE SITE RNFR* RNTO* DELE* DESC#");
1406: sockprintf(sock," FEAT# OPTS#");
1407: sockprintf(sock,"214 Direct comments to sysop@%s.",scfg.sys_inetaddr);
1408: continue;
1409: }
1410: if(!stricmp(cmd, "FEAT")) {
1411: sockprintf(sock,"211-The following additional (post-RFC949) features are supported:");
1412: sockprintf(sock," DESC");
1413: sockprintf(sock," MDTM");
1414: sockprintf(sock," SIZE");
1415: sockprintf(sock," REST STREAM");
1416: sockprintf(sock,"211 End");
1417: continue;
1418: }
1419: if(!strnicmp(cmd, "OPTS",4)) {
1420: sockprintf(sock,"501 No options supported.");
1421: continue;
1422: }
1423: if(!stricmp(cmd, "QUIT")) {
1424: sprintf(str,"%sftpbye.txt",scfg.text_dir);
1425: if((fp=fopen(str,"rb"))!=NULL) {
1426: i=0;
1427: while(!feof(fp)) {
1428: if(!fgets(buf,sizeof(buf),fp))
1429: break;
1430: truncsp(buf);
1431: if(!i)
1432: sockprintf(sock,"221-%s",buf);
1433: else
1434: sockprintf(sock," %s",buf);
1435: i++;
1436: }
1437: fclose(fp);
1438: }
1439: sockprintf(sock,"221 Goodbye. Closing control connection.");
1440: break;
1441: }
1442: if(!strnicmp(cmd, "USER ",5)) {
1443: sysop=FALSE;
1444: user.number=0;
1445: p=cmd+5;
1446: while(*p && *p<=' ') p++;
1447: truncsp(p);
1448: sprintf(user.alias,"%.*s",(int)sizeof(user.alias)-1,p);
1449: if(!stricmp(user.alias,"anonymous"))
1450: strcpy(user.alias,"guest");
1451: user.number=matchuser(&scfg,user.alias);
1452: if(user.number && getuserdat(&scfg, &user)==0 && user.pass[0]==0)
1453: sockprintf(sock,"331 User name okay, give your full e-mail address as password.");
1454: else
1455: sockprintf(sock,"331 User name okay, need password.");
1456: continue;
1457: }
1458: if(!strnicmp(cmd, "PASS ",5) && user.alias[0]) {
1459: user.number=0;
1460: p=cmd+5;
1461: while(*p && *p<=' ') p++;
1462:
1463: sprintf(password,"%.*s",(int)sizeof(password)-1,p);
1464: user.number=matchuser(&scfg,user.alias);
1465: if(!user.number) {
1466: lprintf("%04d !FTP: UNKNOWN USER: %s",sock,user.alias);
1467: sockprintf(sock,"530 Password not accepted.");
1468: continue;
1469: }
1470: if((i=getuserdat(&scfg, &user))!=0) {
1471: lprintf("%04d !FTP: ERROR %d getting data on user #%d (%s)"
1472: ,sock,i,user.number,user.alias);
1473: sockprintf(sock,"530 Database error %d",i);
1474: user.number=0;
1475: continue;
1476: }
1477: if(user.misc&(DELETED|INACTIVE)) {
1478: lprintf("%04d !FTP: DELETED or INACTIVE user #%d (%s)"
1479: ,sock,user.number,user.alias);
1480: sockprintf(sock,"530 Password not accepted.");
1481: user.number=0;
1482: continue;
1483: }
1484: if(user.rest&FLAG('T')) {
1485: lprintf("%04d !FTP: 'T' RESTRICTED user #%d (%s)"
1486: ,sock,user.number,user.alias);
1487: sockprintf(sock,"530 Password not accepted.");
1488: user.number=0;
1489: continue;
1490: }
1491: if(user.ltoday>scfg.level_callsperday[user.level]
1492: && !(user.exempt&FLAG('L'))) {
1493: lprintf("%04d !FTP: Max logons (%d) reached"
1494: ,sock,scfg.level_callsperday[user.level]);
1495: sockprintf(sock,"530 Maximum logons reached.");
1496: user.number=0;
1497: continue;
1498: }
1499: if(user.rest&FLAG('L') && user.ltoday>1) {
1500: lprintf("%04d !FTP: L Restricted user already on today"
1501: ,sock);
1502: sockprintf(sock,"530 Maximum logons reached.");
1503: user.number=0;
1504: continue;
1505: }
1506:
1507: sprintf(sys_pass,"%s:%s",user.pass,scfg.sys_pass);
1508: if(!user.pass[0]) { /* Guest/Anonymous */
1509: lprintf("%04d Guest: %s",sock,password);
1510: putuserrec(&scfg,user.number,U_NETMAIL,LEN_NETMAIL,password);
1511: }
1512: else if(user.level>=SYSOP_LEVEL && !stricmp(password,sys_pass)) {
1513: lprintf("%04d Sysop access granted to %s", sock, user.alias);
1514: sysop=TRUE;
1515: }
1516: else if(stricmp(password,user.pass)) {
1517: lprintf("%04d !FTP: WRONG PASSWORD for user %s: '%s' expected '%s'"
1518: ,sock,user.alias,password,user.pass);
1519: sockprintf(sock,"530 Password not accepted.");
1520: user.number=0;
1521: continue;
1522: }
1523:
1524: /* Update client display */
1525: client.user=user.alias;
1526: client_on(sock,&client);
1527:
1528:
1529: lprintf("%04d %s logged in.",sock,user.alias);
1530: logintime=time(NULL);
1531: timeleft=gettimeleft(&scfg,&user,logintime);
1532: sprintf(str,"%sftphello.txt",scfg.text_dir);
1533: if((fp=fopen(str,"rb"))!=NULL) {
1534: i=0;
1535: while(!feof(fp)) {
1536: if(!fgets(buf,sizeof(buf),fp))
1537: break;
1538: truncsp(buf);
1539: if(!i)
1540: sockprintf(sock,"230-%s",buf);
1541: else
1542: sockprintf(sock," %s",buf);
1543: i++;
1544: }
1545: fclose(fp);
1546: }
1547: if(sysop)
1548: sockprintf(sock,"230-Sysop access granted.");
1549: sockprintf(sock,"230-%s logged in.",user.alias);
1550: if(!(user.exempt&FLAG('D')) && (user.cdt+user.freecdt)>0)
1551: sockprintf(sock,"230-You have %lu download credits."
1552: ,user.cdt+user.freecdt);
1553: sockprintf(sock,"230 You are allowed %ld minutes of use for this session."
1554: ,timeleft/60);
1555: sprintf(qwkfile,"%sfile/%04d.qwk",scfg.data_dir,user.number);
1556:
1557: /* Adjust User Total Logons/Logons Today */
1558: adjustuserrec(&scfg,user.number,U_LOGONS,5,1);
1559: putuserrec(&scfg,user.number,U_LTODAY,5,ultoa(user.ltoday+1,str,10));
1560: putuserrec(&scfg,user.number,U_MODEM,LEN_MODEM,"FTP");
1561: putuserrec(&scfg,user.number,U_COMP,LEN_COMP,host_name);
1562: putuserrec(&scfg,user.number,U_NOTE,LEN_NOTE,host_ip);
1563:
1564: continue;
1565: }
1566:
1567: if(!user.number) {
1568: sockprintf(sock,"530 Please login with USER and PASS.");
1569: continue;
1570: }
1571:
1572: if((timeleft=gettimeleft(&scfg,&user,logintime))<1L) {
1573: sockprintf(sock,"421 Sorry, you've run out of time.");
1574: lprintf("%04d Out of time, disconnecting",sock);
1575: break;
1576: }
1577:
1578: /********************************/
1579: /* These commands require login */
1580: /********************************/
1581:
1582: if(!stricmp(cmd, "REIN")) {
1583: lprintf("%04d %s reinitialized control session",sock,user.alias);
1584: user.number=0;
1585: sysop=FALSE;
1586: filepos=0;
1587: sockprintf(sock,"220 Control session re-initialized. Ready for re-login.");
1588: continue;
1589: }
1590:
1591: if(!stricmp(cmd, "SITE WHO")) {
1592: sockprintf(sock,"211-Active users");
1593: for(i=0;i<scfg.sys_nodes && i<scfg.sys_lastnode;i++) {
1594: if((result=getnodedat(&scfg, i+1, &node, 0))!=0) {
1595: sockprintf(sock," Error %d getting data for Telnet Node %d",result,i+1);
1596: continue;
1597: }
1598: if(node.status==NODE_INUSE)
1599: sockprintf(sock," Telnet Node %3d: %s",i+1, username(&scfg,node.useron,str));
1600: }
1601: sockprintf(sock,"211 End");
1602: continue;
1603: }
1604:
1605: if(!strnicmp(cmd, "PORT ",5)) {
1606: p=cmd+5;
1607: while(*p && *p<=' ') p++;
1608: sscanf(p,"%ld,%ld,%ld,%ld,%hd,%hd",&h1,&h2,&h3,&h4,&p1,&p2);
1609: data_addr.sin_addr.s_addr=htonl((h1<<24)|(h2<<16)|(h3<<8)|h4);
1610: data_addr.sin_port=htons((u_short)((p1<<8)|p2));
1611: sockprintf(sock,"200 PORT Command successful.");
1612: continue;
1613: }
1614:
1615: if(!strcmp(cmd, "PASV")) {
1616:
1617: if(pasv_sock!=INVALID_SOCKET)
1618: close_socket(&pasv_sock,__LINE__);
1619:
1620: if((pasv_sock=open_socket(SOCK_STREAM))==INVALID_SOCKET) {
1621: lprintf("%04d !PASV ERROR %d opening socket", sock,ERROR_VALUE);
1622: sockprintf(sock,"425 Error %d opening PASV data socket", ERROR_VALUE);
1623: continue;
1624: }
1625:
1626: if(startup->options&FTP_OPT_DEBUG_DATA)
1627: lprintf("%04d PASV DATA socket %d opened",sock,pasv_sock);
1628:
1629: pasv_addr.sin_port = 0;
1630:
1631: if((result=bind(pasv_sock, (struct sockaddr *) &pasv_addr,sizeof(pasv_addr)))!= 0) {
1632: lprintf("%04d !PASV ERROR %d (%d) binding socket", sock, result, ERROR_VALUE);
1633: sockprintf(sock,"425 Error %d binding data socket",ERROR_VALUE);
1634: close_socket(&pasv_sock,__LINE__);
1635: continue;
1636: }
1637:
1638: addr_len=sizeof(addr);
1639: if((result=getsockname(pasv_sock, (struct sockaddr *)&addr,&addr_len))!=0) {
1640: lprintf("%04d !PASV ERROR %d (%d) getting address/port", sock, result, ERROR_VALUE);
1641: sockprintf(sock,"425 Error %d getting address/port",ERROR_VALUE);
1642: close_socket(&pasv_sock,__LINE__);
1643: continue;
1644: }
1645:
1646: if((result=listen(pasv_sock, 1))!= 0) {
1647: lprintf("%04d !PASV ERROR %d (%d) listening on socket", sock, result, ERROR_VALUE);
1648: sockprintf(sock,"425 Error %d listening on data socket",ERROR_VALUE);
1649: close_socket(&pasv_sock,__LINE__);
1650: continue;
1651: }
1652:
1653: ip_addr=ntohl(pasv_addr.sin_addr.s_addr);
1654: port=ntohs(addr.sin_port);
1655: sockprintf(sock,"227 Entering Passive Mode (%d,%d,%d,%d,%hd,%hd)"
1656: ,(ip_addr>>24)&0xff
1657: ,(ip_addr>>16)&0xff
1658: ,(ip_addr>>8)&0xff
1659: ,ip_addr&0xff
1660: ,(port>>8)&0xff
1661: ,port&0xff
1662: );
1663: continue;
1664: }
1665:
1666: if(!strnicmp(cmd, "TYPE ",5)) {
1667: sockprintf(sock,"200 All files sent in BINARY mode.");
1668: continue;
1669: }
1670:
1671: if(!strnicmp(cmd, "ALLO",4)) {
1672: p=cmd+5;
1673: while(*p && *p<=' ') p++;
1674: if(*p)
1675: l=atol(p);
1676: else
1677: l=0;
1678: if(local_fsys)
1679: avail=getfreediskspace(local_dir);
1680: else
1681: avail=getfreediskspace(scfg.data_dir); /* Change to temp_dir? */
1682: if(l && l>avail)
1683: sockprintf(sock,"504 Only %ld bytes available.",avail);
1684: else
1685: sockprintf(sock,"200 %ld bytes available.",avail);
1686: continue;
1687: }
1688:
1689: if(!strnicmp(cmd, "REST",4)) {
1690: p=cmd+4;
1691: while(*p && *p<=' ') p++;
1692: if(*p)
1693: filepos=atol(p);
1694: else
1695: filepos=0;
1696: sockprintf(sock,"200 File marker set to %ld",filepos);
1697: continue;
1698: }
1699:
1700: if(!strnicmp(cmd, "MODE ",5)) {
1701: p=cmd+5;
1702: while(*p && *p<=' ') p++;
1703: if(toupper(*p)!='S')
1704: sockprintf(sock,"504 Only STREAM mode supported.");
1705: else
1706: sockprintf(sock,"200 STREAM mode.");
1707: continue;
1708: }
1709:
1710: if(!strnicmp(cmd, "STRU ",5)) {
1711: p=cmd+5;
1712: while(*p && *p<=' ') p++;
1713: if(toupper(*p)!='F')
1714: sockprintf(sock,"504 Only FILE structure supported.");
1715: else
1716: sockprintf(sock,"200 FILE structure.");
1717: continue;
1718: }
1719:
1720: if(!stricmp(cmd, "SYST")) {
1721: sockprintf(sock,"215 UNIX Type: L8");
1722: continue;
1723: }
1724:
1725: if(!stricmp(cmd, "ABOR")) {
1726: if(!transfer_inprogress)
1727: sockprintf(sock,"226 No tranfer in progress.");
1728: else {
1729: transfer_aborted=TRUE;
1730: mswait(1); /* give send thread time to abort */
1731: sockprintf(sock,"226 Transfer aborted.");
1732: }
1733: continue;
1734: }
1735:
1736: if(!strnicmp(cmd,"SMNT ",5) && sysop && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)) {
1737: p=cmd+5;
1738: while(*p && *p<=' ') p++;
1739: if(!stricmp(p,BBS_FSYS_DIR))
1740: local_fsys=FALSE;
1741: else {
1742: if(!direxist(p)) {
1743: sockprintf(sock,"550 Directory does not exist.");
1744: lprintf("%04d !%s attempted to mount invalid directory: %s"
1745: ,sock, user.alias, p);
1746: continue;
1747: }
1748: local_fsys=TRUE;
1749: sprintf(local_dir,"%.*s",(int)sizeof(local_dir)-1,p);
1750: }
1751: sockprintf(sock,"250 %s file system mounted."
1752: ,local_fsys ? "Local" : "BBS");
1753: lprintf("%04d %s mounted %s file system"
1754: ,sock, user.alias, local_fsys ? "local" : "BBS");
1755: continue;
1756: }
1757:
1758: /****************************/
1759: /* Local File System Access */
1760: /****************************/
1761: if(sysop && local_fsys && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)) {
1762: if(local_dir[0]
1763: && local_dir[strlen(local_dir)-1]!='\\'
1764: && local_dir[strlen(local_dir)-1]!='/')
1765: strcat(local_dir,"/");
1766:
1767: if(!strnicmp(cmd, "LIST", 4) || !strnicmp(cmd, "NLST", 4)) {
1768: sprintf(fname,"%sftp%d.tx", scfg.data_dir, sock);
1769: if((fp=fopen(fname,"w+b"))==NULL) {
1770: lprintf("%04d !ERROR %d opening %s",sock,errno,fname);
1771: sockprintf(sock, "451 Insufficient system storage");
1772: continue;
1773: }
1774: if(!strnicmp(cmd, "LIST", 4))
1775: detail=TRUE;
1776: else
1777: detail=FALSE;
1778:
1779: p=cmd+4;
1780: while(*p && *p<=' ') p++;
1781: sprintf(path,"%s%s",local_dir, *p ? p : "*");
1782: lprintf("%04d %s listing: %s", sock, user.alias, path);
1783: sockprintf(sock, "150 Directory of %s%s", local_dir, p);
1784:
1785: glob(path,0,NULL,&g);
1786: for(i=0;i<(int)g.gl_pathc;i++) {
1787: if(detail) {
1788: f.size=flength(g.gl_pathv[i]);
1789: t=fdate(g.gl_pathv[i]);
1790: tm_p=localtime(&t);
1791: if(tm_p==NULL)
1792: memset(&tm,0,sizeof(tm));
1793: else
1794: tm=*tm_p;
1795: fprintf(fp,"%crw-r--r-- 1 %-8s local %9ld %s %2d "
1796: ,isdir(g.gl_pathv[i]) ? 'd':'-'
1797: ,scfg.sys_id
1798: ,f.size
1799: ,mon[tm.tm_mon],tm.tm_mday);
1800: if(tm.tm_year==cur_tm.tm_year)
1801: fprintf(fp,"%02d:%02d %s\r\n"
1802: ,tm.tm_hour,tm.tm_min
1803: ,getfname(g.gl_pathv[i]));
1804: else
1805: fprintf(fp,"%5d %s\r\n"
1806: ,1900+tm.tm_year
1807: ,getfname(g.gl_pathv[i]));
1808: } else
1809: fprintf(fp,"%s\r\n",getfname(g.gl_pathv[i]));
1810: }
1811: globfree(&g);
1812: fclose(fp);
1813: filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,0L
1814: ,&transfer_inprogress,&transfer_aborted
1815: ,TRUE /* delfile */
1816: ,TRUE /* tmpfile */
1817: ,&lastactive,&user,-1,FALSE,FALSE,FALSE,NULL);
1818: continue;
1819: } /* Local LIST/NLST */
1820:
1821: if(!strnicmp(cmd, "CWD ", 4) || !strnicmp(cmd,"XCWD ",5)) {
1822: if(!strnicmp(cmd,"CWD ",4))
1823: p=cmd+4;
1824: else
1825: p=cmd+5;
1826: while(*p && *p<=' ') p++;
1827: tp=p;
1828: if(*tp=='/' || *tp=='\\') /* /local: and /bbs: are valid */
1829: tp++;
1830: if(!strnicmp(tp,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR))) {
1831: local_fsys=FALSE;
1832: sockprintf(sock,"250 CWD command successful (BBS file system mounted).");
1833: lprintf("%04d %s mounted BBS file system", sock, user.alias);
1834: continue;
1835: }
1836: if(!strnicmp(tp,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) {
1837: tp+=strlen(LOCAL_FSYS_DIR); /* already mounted */
1838: p=tp;
1839: }
1840:
1841: if(p[1]==':' || !strncmp(p,"\\\\",2))
1842: strcpy(path,p);
1843: else if(*p=='/' || *p=='\\')
1844: sprintf(path,"%s%s",root_dir(local_dir),p);
1845: else {
1846: sprintf(fname,"%s%s",local_dir,p);
1847: _fullpath(path,fname,sizeof(path));
1848: }
1849:
1850: if(!direxist(path)) {
1851: sockprintf(sock,"550 Directory does not exist (%s).",path);
1852: lprintf("%04d !%s attempted to change to an invalid directory: %s"
1853: ,sock, user.alias, path);
1854: } else {
1855: strcpy(local_dir,path);
1856: sockprintf(sock,"250 CWD command successful (%s).", local_dir);
1857: }
1858: continue;
1859: } /* Local CWD */
1860:
1861: if(!stricmp(cmd,"CDUP") || !stricmp(cmd,"XCUP")) {
1862: sprintf(path,"%s..",local_dir);
1863: if(_fullpath(local_dir,path,sizeof(local_dir))==NULL)
1864: sockprintf(sock,"550 Directory does not exist.");
1865: else
1866: sockprintf(sock,"200 CDUP command successful.");
1867: continue;
1868: }
1869:
1870: if(!stricmp(cmd, "PWD") || !stricmp(cmd,"XPWD")) {
1871: if(strlen(local_dir)>3)
1872: local_dir[strlen(local_dir)-1]=0; /* truncate '/' */
1873:
1874: sockprintf(sock,"257 \"%s\" is current directory."
1875: ,local_dir);
1876: continue;
1877: } /* Local PWD */
1878:
1879: if(!strnicmp(cmd, "MKD ", 4) || !strnicmp(cmd,"XMKD",4)) {
1880: p=cmd+4;
1881: while(*p && *p<=' ') p++;
1882: if(*p=='/') /* absolute */
1883: sprintf(fname,"%s%s",root_dir(local_dir),p+1);
1884: else /* relative */
1885: sprintf(fname,"%s%s",local_dir,p);
1886:
1887: if((i=_mkdir(fname))==0) {
1888: sockprintf(sock,"257 \"%s\" directory created",fname);
1889: lprintf("%04d %s created directory: %s",sock,user.alias,fname);
1890: } else {
1891: sockprintf(sock,"521 Error %d creating directory: %s",i,fname);
1892: lprintf("%04d !%s attempted to create directory: %s (Error %d)"
1893: ,sock,user.alias,fname,i);
1894: }
1895: continue;
1896: }
1897:
1898: if(!strnicmp(cmd, "RMD ", 4) || !strnicmp(cmd,"XRMD",4)) {
1899: p=cmd+4;
1900: while(*p && *p<=' ') p++;
1901: if(*p=='/') /* absolute */
1902: sprintf(fname,"%s%s",root_dir(local_dir),p+1);
1903: else /* relative */
1904: sprintf(fname,"%s%s",local_dir,p);
1905:
1906: if((i=_rmdir(fname))==0) {
1907: sockprintf(sock,"250 \"%s\" directory removed",fname);
1908: lprintf("%04d %s removed directory: %s",sock,user.alias,fname);
1909: } else {
1910: sockprintf(sock,"450 Error %d removing directory: %s",i,fname);
1911: lprintf("%04d !%s attempted to remove directory: %s (Error %d)"
1912: ,sock,user.alias,fname,i);
1913: }
1914: continue;
1915: }
1916:
1917: if(!strnicmp(cmd, "RNFR ",5)) {
1918: p=cmd+5;
1919: while(*p && *p<=' ') p++;
1920: if(*p=='/') /* absolute */
1921: sprintf(ren_from,"%s%s",root_dir(local_dir),p+1);
1922: else /* relative */
1923: sprintf(ren_from,"%s%s",local_dir,p);
1924: if(!fexist(ren_from)) {
1925: sockprintf(sock,"550 File not found: %s",ren_from);
1926: lprintf("%04d !%s attempted to rename %s (not found)"
1927: ,sock,user.alias,ren_from);
1928: } else
1929: sockprintf(sock,"350 File exists, ready for destination name");
1930: continue;
1931: }
1932:
1933: if(!strnicmp(cmd, "RNTO ",5)) {
1934: p=cmd+5;
1935: while(*p && *p<=' ') p++;
1936: if(*p=='/') /* absolute */
1937: sprintf(fname,"%s%s",root_dir(local_dir),p+1);
1938: else /* relative */
1939: sprintf(fname,"%s%s",local_dir,p);
1940:
1941: if((i=rename(ren_from, fname))==0) {
1942: sockprintf(sock,"250 \"%s\" renamed to \"%s\"",ren_from,fname);
1943: lprintf("%04d %s renamed %s to %s",sock,user.alias,ren_from,fname);
1944: } else {
1945: sockprintf(sock,"450 Error %d renaming file: %s",i,ren_from);
1946: lprintf("%04d !%s attempted to rename file: %s (Error %d)"
1947: ,sock,user.alias,ren_from,i);
1948: }
1949: continue;
1950: }
1951:
1952:
1953: if(!strnicmp(cmd, "RETR ", 5) || !strnicmp(cmd,"SIZE ",5)
1954: || !strnicmp(cmd, "MDTM ",5) || !strnicmp(cmd, "DELE ",5)) {
1955: p=cmd+5;
1956: while(*p && *p<=' ') p++;
1957:
1958: if(!strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR)))
1959: p+=strlen(LOCAL_FSYS_DIR); /* already mounted */
1960:
1961: if(p[1]==':') /* drive specified */
1962: strcpy(fname,p);
1963: else if(*p=='/') /* absolute, current drive */
1964: sprintf(fname,"%s%s",root_dir(local_dir),p+1);
1965: else /* relative */
1966: sprintf(fname,"%s%s",local_dir,p);
1967: if(!fexist(fname)) {
1968: lprintf("%04d !%s file not found: %s",sock,user.alias,fname);
1969: sockprintf(sock,"550 File not found: %s",fname);
1970: continue;
1971: }
1972: if(!strnicmp(cmd,"SIZE ",5)) {
1973: sockprintf(sock,"213 %lu",flength(fname));
1974: continue;
1975: }
1976: if(!strnicmp(cmd,"MDTM ",5)) {
1977: t=fdate(fname);
1978: tm_p=gmtime(&t);
1979: if(tm_p==NULL)
1980: memset(&tm,0,sizeof(tm));
1981: else
1982: tm=*tm_p;
1983: sockprintf(sock,"213 %u%02u%02u%02u%02u%02u"
1984: ,1900+tm.tm_year,tm.tm_mon+1,tm.tm_mday
1985: ,tm.tm_hour,tm.tm_min,tm.tm_sec);
1986: continue;
1987: }
1988: if(!strnicmp(cmd,"DELE ",5)) {
1989: if((i=remove(fname))==0) {
1990: sockprintf(sock,"250 \"%s\" removed successfully.",fname);
1991: lprintf("%04d %s deleted file: %s",sock,user.alias,fname);
1992: } else {
1993: sockprintf(sock,"450 Error %d removing file: %s",i,fname);
1994: lprintf("%04d !%s attempted to delete file: %s (Error %d)"
1995: ,sock,user.alias,fname,i);
1996: }
1997: continue;
1998: }
1999: /* RETR */
2000: lprintf("%04d %s downloading: %s (%ld bytes)"
2001: ,sock,user.alias,fname,flength(fname));
2002: sockprintf(sock,"150 Opening BINARY mode data connection for file transfer.");
2003: filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,filepos
2004: ,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
2005: ,&lastactive,&user,-1,FALSE,FALSE,FALSE,NULL);
2006: continue;
2007: } /* Local RETR/SIZE/MDTM */
2008:
2009: if(!strnicmp(cmd, "STOR ", 5) || !strnicmp(cmd, "APPE ", 5)) {
2010: p=cmd+5;
2011: while(*p && *p<=' ') p++;
2012:
2013: if(!strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR)))
2014: p+=strlen(LOCAL_FSYS_DIR); /* already mounted */
2015:
2016: if(p[1]==':') /* drive specified */
2017: strcpy(fname,p);
2018: else if(*p=='/') /* absolute, current drive */
2019: sprintf(fname,"%s%s",root_dir(local_dir),p+1);
2020: else /* relative */
2021: sprintf(fname,"%s%s",local_dir,p);
2022:
2023: lprintf("%04d %s uploading %s", sock,user.alias,fname);
2024: sockprintf(sock,"150 Opening BINARY mode data connection for file transfer.");
2025: filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,filepos
2026: ,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
2027: ,&lastactive
2028: ,&user
2029: ,-1 /* dir */
2030: ,TRUE /* uploading */
2031: ,FALSE /* credits */
2032: ,!strnicmp(cmd,"APPE",4) ? TRUE : FALSE /* append */
2033: ,NULL /* desc */
2034: );
2035: filepos=0;
2036: continue;
2037: } /* Local STOR */
2038: }
2039:
2040: if(!strnicmp(cmd, "LIST", 4) || !strnicmp(cmd, "NLST", 4)) {
2041: dir=curdir;
2042: lib=curlib;
2043:
2044: if(cmd[4]!=0)
2045: lprintf("%04d LIST/NLST: %s",sock,cmd);
2046:
2047: /* path specified? */
2048: p=cmd+4;
2049: while(*p && *p<=' ') p++;
2050:
2051: if(*p=='-') { /* -Letc */
2052: while(*p && *p>' ') p++;
2053: while(*p && *p<=' ') p++;
2054: }
2055:
2056: parsepath(&p,&user,&lib,&dir);
2057:
2058: sprintf(fname,"%sftp%d.tx", scfg.data_dir, sock);
2059: if((fp=fopen(fname,"w+b"))==NULL) {
2060: lprintf("%04d !ERROR %d opening %s",sock,errno,fname);
2061: sockprintf(sock, "451 Insufficient system storage");
2062: continue;
2063: }
2064: if(!strnicmp(cmd, "LIST", 4))
2065: detail=TRUE;
2066: else
2067: detail=FALSE;
2068: sockprintf(sock,"150 Opening ASCII mode data connection for /bin/ls.");
2069: now=time(NULL);
2070: tm_p=localtime(&now);
2071: if(tm_p==NULL)
2072: memset(&cur_tm,0,sizeof(cur_tm));
2073: else
2074: cur_tm=*tm_p;
2075:
2076: if(startup->options&FTP_OPT_INDEX_FILE && startup->index_file_name[0]
2077: && (!stricmp(p,startup->index_file_name) || *p==0 || *p=='*')) {
2078: if(detail)
2079: fprintf(fp,"-rw-r--r-- 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
2080: ,NAME_LEN
2081: ,scfg.sys_id
2082: ,lib<0 ? scfg.sys_id : dir<0
2083: ? scfg.lib[lib]->sname : scfg.dir[dir]->code
2084: ,512L
2085: ,mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
2086: ,startup->index_file_name);
2087: else
2088: fprintf(fp,"%s\r\n",startup->index_file_name);
2089: }
2090: if(lib<0) { /* Root dir */
2091: lprintf("%04d %s listing: root",sock,user.alias);
2092:
2093: /* QWK Packet */
2094: if(startup->options&FTP_OPT_ALLOW_QWK/* && fexist(qwkfile)*/) {
2095: if(detail) {
2096: if(fexist(qwkfile)) {
2097: t=fdate(qwkfile);
2098: l=flength(qwkfile);
2099: } else {
2100: t=time(NULL);
2101: l=10240;
2102: };
2103: tm_p=localtime(&t);
2104: if(tm_p==NULL)
2105: memset(&tm,0,sizeof(tm));
2106: else
2107: tm=*tm_p;
2108: fprintf(fp,"-rw-r--r-- 1 %-*s %-8s %9ld %s %2d %02d:%02d %s.qwk\r\n"
2109: ,NAME_LEN
2110: ,scfg.sys_id
2111: ,scfg.sys_id
2112: ,l
2113: ,mon[tm.tm_mon],tm.tm_mday,tm.tm_hour,tm.tm_min
2114: ,scfg.sys_id);
2115: } else
2116: fprintf(fp,"%s.qwk\r\n",scfg.sys_id);
2117: }
2118:
2119: /* File Aliases */
2120: sprintf(aliasfile,"%sftpalias.cfg",scfg.ctrl_dir);
2121: if((alias_fp=fopen(aliasfile,"r"))!=NULL) {
2122:
2123: while(!feof(alias_fp)) {
2124: if(!fgets(aliasline,sizeof(aliasline)-1,alias_fp))
2125: break;
2126:
2127: alias_dir=FALSE;
2128:
2129: p=aliasline; /* alias pointer */
2130: while(*p && *p<=' ') p++;
2131:
2132: if(*p==';') /* comment */
2133: continue;
2134:
2135: tp=p; /* terminator pointer */
2136: while(*tp && *tp>' ') tp++;
2137: if(*tp) *tp=0;
2138:
2139: np=tp+1; /* filename pointer */
2140: while(*np && *np<=' ') np++;
2141:
2142: tp=np; /* terminator pointer */
2143: while(*tp && *tp>' ') tp++;
2144: if(*tp) *tp=0;
2145:
2146: /* Virtual Path? */
2147: if(!strnicmp(np,BBS_VIRTUAL_PATH,strlen(BBS_VIRTUAL_PATH))) {
2148: if((dir=getdir(np+strlen(BBS_VIRTUAL_PATH),&user))<0)
2149: continue; /* No access or invalid virtual path */
2150: tp=strrchr(np,'/');
2151: if(tp==NULL)
2152: continue;
2153: tp++;
2154: if(*tp) {
2155: sprintf(aliasfile,"%s%s",scfg.dir[dir]->path,tp);
2156: np=aliasfile;
2157: }
2158: else
2159: alias_dir=TRUE;
2160: }
2161:
2162: if(!alias_dir && !fexist(np))
2163: continue;
2164:
2165: if(detail) {
2166:
2167: if(alias_dir==TRUE) {
2168: fprintf(fp,"drwxr-xr-x 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
2169: ,NAME_LEN
2170: ,scfg.sys_id
2171: ,scfg.lib[scfg.dir[dir]->lib]->sname
2172: ,512L
2173: ,mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
2174: ,p);
2175: }
2176: else {
2177: t=fdate(np);
2178: tm_p=localtime(&t);
2179: if(tm_p==NULL)
2180: memset(&tm,0,sizeof(tm));
2181: else
2182: tm=*tm_p;
2183: fprintf(fp,"-rw-r--r-- 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
2184: ,NAME_LEN
2185: ,scfg.sys_id
2186: ,scfg.sys_id
2187: ,flength(np)
2188: ,mon[tm.tm_mon],tm.tm_mday,tm.tm_hour,tm.tm_min
2189: ,p);
2190: }
2191: } else
2192: fprintf(fp,"%s\r\n",p);
2193:
2194: }
2195:
2196: fclose(alias_fp);
2197: }
2198:
2199: /* Library folders */
2200: for(i=0;i<scfg.total_libs;i++) {
2201: if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
2202: continue;
2203: if(detail)
2204: fprintf(fp,"drwxr-xr-x 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
2205: ,NAME_LEN
2206: ,scfg.sys_id
2207: ,scfg.sys_id
2208: ,512L
2209: ,mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
2210: ,scfg.lib[i]->sname);
2211: else
2212: fprintf(fp,"%s\r\n",scfg.lib[i]->sname);
2213: }
2214: } else if(dir<0) {
2215: lprintf("%04d %s listing: %s library",sock,user.alias,scfg.lib[lib]->sname);
2216: for(i=0;i<scfg.total_dirs;i++) {
2217: if(scfg.dir[i]->lib!=lib)
2218: continue;
2219: if(i!=scfg.sysop_dir && i!=scfg.upload_dir
2220: && !chk_ar(&scfg,scfg.dir[i]->ar,&user))
2221: continue;
2222: if(detail)
2223: fprintf(fp,"drwxr-xr-x 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
2224: ,NAME_LEN
2225: ,scfg.sys_id
2226: ,scfg.lib[lib]->sname
2227: ,512L
2228: ,mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
2229: ,scfg.dir[i]->code);
2230: else
2231: fprintf(fp,"%s\r\n",scfg.dir[i]->code);
2232: }
2233: } else if(chk_ar(&scfg,scfg.dir[dir]->ar,&user)) {
2234: lprintf("%04d %s listing: %s/%s directory"
2235: ,sock,user.alias,scfg.lib[lib]->sname,scfg.dir[dir]->code);
2236:
2237: sprintf(path,"%s%s",scfg.dir[dir]->path,*p ? p : "*");
2238: glob(path,0,NULL,&g);
2239: for(i=0;i<(int)g.gl_pathc;i++) {
2240: if(isdir(g.gl_pathv[i]))
2241: continue;
2242: #ifdef _WIN32
2243: GetShortPathName(g.gl_pathv[i], str, sizeof(str));
2244: #else
2245: strcpy(str,g.gl_pathv[i]);
2246: #endif
2247: padfname(getfname(str),f.name);
2248: strupr(f.name);
2249: f.dir=dir;
2250: if((filedat=getfileixb(&scfg,&f))==FALSE
2251: && !(startup->options&FTP_OPT_DIR_FILES))
2252: continue;
2253: if(detail) {
2254: f.size=flength(g.gl_pathv[i]);
2255: getfiledat(&scfg,&f);
2256: t=fdate(g.gl_pathv[i]);
2257: tm_p=localtime(&t);
2258: if(tm_p==NULL)
2259: memset(&tm,0,sizeof(tm));
2260: else
2261: tm=*tm_p;
2262: fprintf(fp,"-rw-r--r-- 1 %-*s %-8s %9ld %s %2d "
2263: ,NAME_LEN
2264: ,filedat ? dotname(f.uler,str) : scfg.sys_id
2265: ,scfg.dir[dir]->code
2266: ,f.size
2267: ,mon[tm.tm_mon],tm.tm_mday);
2268: if(tm.tm_year==cur_tm.tm_year)
2269: fprintf(fp,"%02d:%02d %s\r\n"
2270: ,tm.tm_hour,tm.tm_min
2271: ,getfname(g.gl_pathv[i]));
2272: else
2273: fprintf(fp,"%5d %s\r\n"
2274: ,1900+tm.tm_year
2275: ,getfname(g.gl_pathv[i]));
2276: } else
2277: fprintf(fp,"%s\r\n",getfname(g.gl_pathv[i]));
2278: }
2279: globfree(&g);
2280: } else
2281: lprintf("%04d %s listing: %s/%s directory (empty - no access)"
2282: ,sock,user.alias,scfg.lib[lib]->sname,scfg.dir[dir]->code);
2283:
2284: fclose(fp);
2285: filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,0L
2286: ,&transfer_inprogress,&transfer_aborted
2287: ,TRUE /* delfile */
2288: ,TRUE /* tmpfile */
2289: ,&lastactive,&user,dir,FALSE,FALSE,FALSE,NULL);
2290: continue;
2291: }
2292:
2293: if(!strnicmp(cmd, "RETR ", 5)
2294: || !strnicmp(cmd, "SIZE ",5)
2295: || !strnicmp(cmd, "MDTM ",5)
2296: || !strnicmp(cmd, "DELE ",5)) {
2297: getdate=FALSE;
2298: getsize=FALSE;
2299: delecmd=FALSE;
2300: if(!strnicmp(cmd,"SIZE ",5))
2301: getsize=TRUE;
2302: else if(!strnicmp(cmd,"MDTM ",5))
2303: getdate=TRUE;
2304: else if(!strnicmp(cmd,"DELE ",5))
2305: delecmd=TRUE;
2306:
2307: if(!getsize && !getdate && user.rest&FLAG('D')) {
2308: sockprintf(sock,"550 Insufficient access.");
2309: filepos=0;
2310: continue;
2311: }
2312: credits=TRUE;
2313: success=FALSE;
2314: delfile=FALSE;
2315: tmpfile=FALSE;
2316: lib=curlib;
2317: dir=curdir;
2318:
2319: p=cmd+5;
2320: while(*p && *p<=' ') p++;
2321:
2322: if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR)))
2323: p+=strlen(BBS_FSYS_DIR); /* already mounted */
2324:
2325: if(*p=='/') {
2326: lib=-1;
2327: p++;
2328: }
2329: else if(!strncmp(p,"./",2))
2330: p+=2;
2331:
2332: if(lib<0 && alias(p, fname, &user, &dir)==TRUE) {
2333: success=TRUE;
2334: credits=TRUE; /* include in d/l stats */
2335: tmpfile=FALSE;
2336: delfile=FALSE;
2337: lprintf("%04d %s %.4s by alias: %s"
2338: ,sock,user.alias,cmd,p);
2339: p=getfname(fname);
2340: if(dir>=0)
2341: lib=scfg.dir[dir]->lib;
2342: }
2343: if(!success && lib<0 && (tp=strchr(p,'/'))!=NULL) {
2344: dir=-1;
2345: *tp=0;
2346: for(i=0;i<scfg.total_libs;i++) {
2347: if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
2348: continue;
2349: if(!stricmp(scfg.lib[i]->sname,p))
2350: break;
2351: }
2352: if(i<scfg.total_libs)
2353: lib=i;
2354: p=tp+1;
2355: }
2356: if(!success && dir<0 && (tp=strchr(p,'/'))!=NULL) {
2357: *tp=0;
2358: for(i=0;i<scfg.total_dirs;i++) {
2359: if(scfg.dir[i]->lib!=lib)
2360: continue;
2361: if(!chk_ar(&scfg,scfg.dir[i]->ar,&user))
2362: continue;
2363: if(!stricmp(scfg.dir[i]->code,p))
2364: break;
2365: }
2366: if(i<scfg.total_dirs)
2367: dir=i;
2368: p=tp+1;
2369: }
2370:
2371: sprintf(str,"%s.qwk",scfg.sys_id);
2372: if(lib<0 && startup->options&FTP_OPT_ALLOW_QWK
2373: && !stricmp(p,str) && !delecmd) {
2374: lprintf("%04d %s creating/updating QWK packet...",sock,user.alias);
2375: sprintf(str,"%spack%04u.now",scfg.data_dir,user.number);
2376: if((file=open(str,O_WRONLY|O_CREAT,S_IWRITE))==-1) {
2377: lprintf("%04d !ERROR %d opening %s",sock, errno, str);
2378: sockprintf(sock, "451 !ERROR %d creating semaphore file",errno);
2379: filepos=0;
2380: continue;
2381: }
2382: close(file);
2383: t=time(NULL);
2384: while(fexist(str)) {
2385: if(time(NULL)-t>300)
2386: break;
2387: mswait(500);
2388: }
2389: if(fexist(str)) {
2390: lprintf("%04d !TIMEOUT waiting for QWK packet creation",sock);
2391: sockprintf(sock,"451 Time-out waiting for packet creation.");
2392: remove(str);
2393: filepos=0;
2394: continue;
2395: }
2396: if(!fexist(qwkfile)) {
2397: lprintf("%04d No QWK Packet created (no new messages)",sock);
2398: sockprintf(sock,"550 No QWK packet created (no new messages)");
2399: filepos=0;
2400: continue;
2401: }
2402: strcpy(fname,qwkfile);
2403: success=TRUE;
2404: delfile=TRUE;
2405: credits=FALSE;
2406: lprintf("%04d %s downloading QWK packet (%ld bytes)"
2407: ,sock,user.alias,flength(fname));
2408: } else if(startup->options&FTP_OPT_INDEX_FILE
2409: && !stricmp(p,startup->index_file_name)
2410: && !delecmd) {
2411: sprintf(fname,"%sftp%d.tx", scfg.data_dir, sock);
2412: if((fp=fopen(fname,"w+b"))==NULL) {
2413: lprintf("%04d !ERROR %d opening %s",sock,errno,fname);
2414: sockprintf(sock, "451 Insufficient system storage");
2415: filepos=0;
2416: continue;
2417: }
2418: if(!getsize && !getdate)
2419: lprintf("%04d %s downloading index",sock,user.alias);
2420: success=TRUE;
2421: credits=FALSE;
2422: tmpfile=TRUE;
2423: delfile=TRUE;
2424: fprintf(fp,"%-*s File/Folder Descriptions\r\n"
2425: ,INDEX_FNAME_LEN,startup->index_file_name);
2426: if(lib<0) {
2427:
2428: /* File Aliases */
2429: sprintf(aliasfile,"%sftpalias.cfg",scfg.ctrl_dir);
2430: if((alias_fp=fopen(aliasfile,"r"))!=NULL) {
2431:
2432: while(!feof(alias_fp)) {
2433: if(!fgets(aliasline,sizeof(aliasline)-1,alias_fp))
2434: break;
2435:
2436: p=aliasline; /* alias pointer */
2437: while(*p && *p<=' ') p++;
2438:
2439: if(*p==';') /* comment */
2440: continue;
2441:
2442: tp=p; /* terminator pointer */
2443: while(*tp && *tp>' ') tp++;
2444: if(*tp) *tp=0;
2445:
2446: np=tp+1; /* filename pointer */
2447: while(*np && *np<=' ') np++;
2448:
2449: np++; /* description pointer */
2450: while(*np && *np>' ') np++;
2451:
2452: while(*np && *np<' ') np++;
2453:
2454: truncsp(np);
2455:
2456: fprintf(fp,"%-*s %s\r\n",INDEX_FNAME_LEN,p,np);
2457: }
2458:
2459: fclose(alias_fp);
2460: }
2461:
2462: /* QWK Packet */
2463: if(startup->options&FTP_OPT_ALLOW_QWK /* && fexist(qwkfile) */) {
2464: sprintf(str,"%s.qwk",scfg.sys_id);
2465: fprintf(fp,"%-*s QWK Message Packet\r\n"
2466: ,INDEX_FNAME_LEN,str);
2467: }
2468:
2469: /* Library Folders */
2470: for(i=0;i<scfg.total_libs;i++) {
2471: if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
2472: continue;
2473: fprintf(fp,"%-*s %s\r\n"
2474: ,INDEX_FNAME_LEN,scfg.lib[i]->sname,scfg.lib[i]->lname);
2475: }
2476: } else if(dir<0) {
2477: for(i=0;i<scfg.total_dirs;i++) {
2478: if(scfg.dir[i]->lib!=lib)
2479: continue;
2480: if(i!=scfg.sysop_dir && i!=scfg.upload_dir
2481: && !chk_ar(&scfg,scfg.dir[i]->ar,&user))
2482: continue;
2483: fprintf(fp,"%-*s %s\r\n"
2484: ,INDEX_FNAME_LEN,scfg.dir[i]->code,scfg.dir[i]->lname);
2485: }
2486: } else {
2487: sprintf(cmd,"%s*",scfg.dir[dir]->path);
2488: glob(cmd,0,NULL,&g);
2489: for(i=0;i<(int)g.gl_pathc;i++) {
2490: if(isdir(g.gl_pathv[i]))
2491: continue;
2492: #ifdef _WIN32
2493: GetShortPathName(g.gl_pathv[i], str, sizeof(str));
2494: #else
2495: strcpy(str,g.gl_pathv[i]);
2496: #endif
2497: padfname(getfname(str),f.name);
2498: strupr(f.name);
2499: f.dir=dir;
2500: if(getfileixb(&scfg,&f)) {
2501: f.size=flength(g.gl_pathv[i]);
2502: getfiledat(&scfg,&f);
2503: fprintf(fp,"%-*s %s\r\n",INDEX_FNAME_LEN
2504: ,getfname(g.gl_pathv[i]),f.desc);
2505: }
2506: }
2507: globfree(&g);
2508: }
2509: fclose(fp);
2510: } else if(dir>=0) {
2511:
2512: if(!chk_ar(&scfg,scfg.dir[dir]->ar,&user)) {
2513: lprintf("%04d !%s has insufficient access to /%s/%s"
2514: ,sock,user.alias,scfg.lib[scfg.dir[dir]->lib]->sname,scfg.dir[dir]->code);
2515: sockprintf(sock,"550 Insufficient access.");
2516: filepos=0;
2517: continue;
2518: }
2519:
2520: if(!getsize && !getdate && !delecmd
2521: && !chk_ar(&scfg,scfg.dir[dir]->dl_ar,&user)) {
2522: lprintf("%04d !%s has insufficient access to download from /%s/%s"
2523: ,sock,user.alias,scfg.lib[scfg.dir[dir]->lib]->sname,scfg.dir[dir]->code);
2524: sockprintf(sock,"550 Insufficient access.");
2525: filepos=0;
2526: continue;
2527: }
2528:
2529: if(delecmd && !dir_op(&scfg,&user,dir)) {
2530: lprintf("%04d !%s has insufficient access to delete files in /%s/%s"
2531: ,sock,user.alias,scfg.lib[scfg.dir[dir]->lib]->sname,scfg.dir[dir]->code);
2532: sockprintf(sock,"550 Insufficient access.");
2533: filepos=0;
2534: continue;
2535: }
2536: sprintf(fname,"%s%s",scfg.dir[dir]->path,p);
2537: #ifdef _WIN32
2538: GetShortPathName(fname, str, sizeof(str));
2539: #else
2540: strcpy(str,fname);
2541: #endif
2542: padfname(getfname(str),f.name);
2543: strupr(f.name);
2544: f.dir=dir;
2545: f.cdt=0;
2546: f.size=-1;
2547: filedat=getfileixb(&scfg,&f);
2548: if(!filedat && !(startup->options&FTP_OPT_DIR_FILES)) {
2549: sockprintf(sock,"550 File not found: %s",p);
2550: lprintf("%04d !%s file not in database (%s) for %.4s command"
2551: ,sock,user.alias,fname,cmd);
2552: filepos=0;
2553: continue;
2554: }
2555:
2556: /* Verify credits */
2557: if(!getsize && !getdate && !delecmd
2558: && !(scfg.dir[dir]->misc&DIR_FREE) && !(user.exempt&FLAG('D'))) {
2559: if(filedat)
2560: getfiledat(&scfg,&f);
2561: else
2562: f.cdt=flength(fname);
2563: if(f.cdt>(user.cdt+user.freecdt)) {
2564: lprintf("%04d !%s has insufficient credit to download /%s/%s/%s (%lu credits)"
2565: ,sock,user.alias,scfg.lib[scfg.dir[dir]->lib]->sname
2566: ,scfg.dir[dir]->code
2567: ,p
2568: ,f.cdt);
2569: sockprintf(sock,"550 Insufficient credit (%lu required).",f.cdt);
2570: filepos=0;
2571: continue;
2572: }
2573: }
2574:
2575: if(strcspn(p,ILLEGAL_FILENAME_CHARS)!=strlen(p)) {
2576: success=FALSE;
2577: lprintf("%04d !%s illegal filename attempt: %s"
2578: ,sock,user.alias,p);
2579: } else {
2580: if(fexist(fname)) {
2581: success=TRUE;
2582: if(!getsize && !getdate && !delecmd)
2583: lprintf("%04d %s downloading: %s (%ld bytes)"
2584: ,sock,user.alias,fname,flength(fname));
2585: }
2586: }
2587: }
2588: if(getsize && success)
2589: sockprintf(sock,"213 %lu",flength(fname));
2590: else if(getdate && success) {
2591: t=fdate(fname);
2592: tm_p=gmtime(&t);
2593: if(tm_p==NULL)
2594: memset(&tm,0,sizeof(tm));
2595: else
2596: tm=*tm_p;
2597: sockprintf(sock,"213 %u%02u%02u%02u%02u%02u"
2598: ,1900+tm.tm_year,tm.tm_mon+1,tm.tm_mday
2599: ,tm.tm_hour,tm.tm_min,tm.tm_sec);
2600: } else if(delecmd && success) {
2601: if(remove(fname)!=0) {
2602: lprintf("%04d !ERROR %d deleting %s",sock,errno,fname);
2603: sockprintf(sock,"450 %s could not be deleted (error: %d)"
2604: ,fname,errno);
2605: } else {
2606: lprintf("%04d %s deleted %s",sock,user.alias,fname);
2607: if(filedat)
2608: removefiledat(&scfg,&f);
2609: sockprintf(sock,"250 %s deleted.",fname);
2610: }
2611: } else if(success) {
2612: sockprintf(sock,"150 Opening BINARY mode data connection for file transfer.");
2613: filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,filepos
2614: ,&transfer_inprogress,&transfer_aborted,delfile,tmpfile
2615: ,&lastactive,&user,dir,FALSE,credits,FALSE,NULL);
2616: }
2617: else {
2618: sockprintf(sock,"550 File not found: %s",p);
2619: lprintf("%04d !%s file not found (%s) for %.4s command"
2620: ,sock,user.alias,p,cmd);
2621: }
2622: filepos=0;
2623: continue;
2624: }
2625:
2626: if(!strnicmp(cmd, "DESC", 4)) {
2627:
2628: if(user.rest&FLAG('U')) {
2629: sockprintf(sock,"553 Insufficient access.");
2630: continue;
2631: }
2632:
2633: p=cmd+4;
2634: while(*p && *p<=' ') p++;
2635:
2636: if(*p==0)
2637: sockprintf(sock,"501 No file description given.");
2638: else {
2639: sprintf(desc,"%.*s",(int)sizeof(desc)-1,p);
2640: sockprintf(sock,"200 File description set. Ready to STOR file.");
2641: }
2642: continue;
2643: }
2644:
2645: if(!strnicmp(cmd, "STOR ", 5)) {
2646:
2647: if(user.rest&FLAG('U')) {
2648: sockprintf(sock,"553 Insufficient access.");
2649: continue;
2650: }
2651:
2652: if(transfer_inprogress==TRUE) {
2653: lprintf("%04d !TRANSFER already in progress (%s)",sock,cmd);
2654: sockprintf(sock,"425 Transfer already in progress.");
2655: continue;
2656: }
2657:
2658: lib=curlib;
2659: dir=curdir;
2660: p=cmd+5;
2661:
2662: while(*p && *p<=' ') p++;
2663:
2664: if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR)))
2665: p+=strlen(BBS_FSYS_DIR); /* already mounted */
2666:
2667: if(*p=='/') {
2668: lib=-1;
2669: p++;
2670: }
2671: else if(!strncmp(p,"./",2))
2672: p+=2;
2673: if(lib<0 && (tp=strchr(p,'/'))!=NULL) {
2674: dir=-1;
2675: *tp=0;
2676: for(i=0;i<scfg.total_libs;i++) {
2677: if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
2678: continue;
2679: if(!stricmp(scfg.lib[i]->sname,p))
2680: break;
2681: }
2682: if(i<scfg.total_libs)
2683: lib=i;
2684: p=tp+1;
2685: }
2686: if(dir<0 && (tp=strchr(p,'/'))!=NULL) {
2687: *tp=0;
2688: for(i=0;i<scfg.total_dirs;i++) {
2689: if(scfg.dir[i]->lib!=lib)
2690: continue;
2691: if(!chk_ar(&scfg,scfg.dir[i]->ar,&user))
2692: continue;
2693: if(!stricmp(scfg.dir[i]->code,p))
2694: break;
2695: }
2696: if(i<scfg.total_dirs)
2697: dir=i;
2698: p=tp+1;
2699: }
2700: if(dir<0) {
2701: sprintf(str,"%s.rep",scfg.sys_id);
2702: if(!(startup->options&FTP_OPT_ALLOW_QWK)
2703: || stricmp(p,str)) {
2704: lprintf("%04d !%s attempted to upload to invalid directory"
2705: ,sock,user.alias);
2706: sockprintf(sock,"553 Invalid directory.");
2707: continue;
2708: }
2709: sprintf(fname,"%sfile/%04d.rep",scfg.data_dir,user.number);
2710: lprintf("%04d %s uploading %s"
2711: ,sock,user.alias,fname);
2712: } else {
2713: if(!chk_ar(&scfg,scfg.dir[dir]->ul_ar,&user)) {
2714: lprintf("%04d !%s has insufficient access to upload to /%s/%s"
2715: ,sock,user.alias,scfg.lib[scfg.dir[dir]->lib]->sname,scfg.dir[dir]->code);
2716: sockprintf(sock,"553 Insufficient access.");
2717: continue;
2718: }
2719: if(strcspn(p,ILLEGAL_FILENAME_CHARS)!=strlen(p)
2720: || trashcan(&scfg,p,"file")) {
2721: lprintf("%04d !%s illegal filename attempt: %s"
2722: ,sock,user.alias,p);
2723: sockprintf(sock,"553 Illegal filename attempt");
2724: continue;
2725: }
2726: sprintf(fname,"%s%s",scfg.dir[dir]->path,p);
2727: if(fexist(fname)) {
2728: lprintf("%04d !%s attempted to overwrite existing file: %s"
2729: ,sock,user.alias,fname);
2730: sockprintf(sock,"553 File already exists.");
2731: continue;
2732: }
2733: lprintf("%04d %s uploading %s to /%s/%s"
2734: ,sock,user.alias,fname
2735: ,scfg.lib[scfg.dir[dir]->lib]->sname,scfg.dir[dir]->code);
2736: }
2737: sockprintf(sock,"150 Opening BINARY mode data connection for file transfer.");
2738: filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,filepos
2739: ,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
2740: ,&lastactive
2741: ,&user
2742: ,dir
2743: ,TRUE /* uploading */
2744: ,TRUE /* credits */
2745: ,FALSE /* append */
2746: ,desc
2747: );
2748: filepos=0;
2749: continue;
2750: }
2751:
2752: if(!stricmp(cmd,"CDUP") || !stricmp(cmd,"XCUP")) {
2753: if(curdir<0)
2754: curlib=-1;
2755: else
2756: curdir=-1;
2757: sockprintf(sock,"200 CDUP command successful.");
2758: continue;
2759: }
2760:
2761: if(!strnicmp(cmd, "CWD ", 4) || !strnicmp(cmd,"XCWD ",5)) {
2762: p=cmd+4;
2763: while(*p && *p<=' ') p++;
2764:
2765: if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR)))
2766: p+=strlen(BBS_FSYS_DIR); /* already mounted */
2767:
2768: if(*p=='/') {
2769: curlib=-1;
2770: curdir=-1;
2771: p++;
2772: }
2773: /* Local File System? */
2774: if(sysop && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)
2775: && !strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) {
2776: p+=strlen(LOCAL_FSYS_DIR);
2777: if(!direxist(p)) {
2778: sockprintf(sock,"550 Directory does not exist.");
2779: lprintf("%04d !%s attempted to mount invalid directory: %s"
2780: ,sock, user.alias, p);
2781: continue;
2782: }
2783: sprintf(local_dir,"%.*s",(int)sizeof(local_dir)-1,p);
2784: local_fsys=TRUE;
2785: sockprintf(sock,"250 CWD command successful (local file system mounted).");
2786: lprintf("%04d %s mounted local file system", sock, user.alias);
2787: continue;
2788: }
2789: success=FALSE;
2790:
2791: /* Directory Alias? */
2792: if(curlib<0 && alias(p,NULL,&user,&curdir)==TRUE) {
2793: if(curdir>=0)
2794: curlib=scfg.dir[curdir]->lib;
2795: success=TRUE;
2796: }
2797:
2798: orglib=curlib;
2799: orgdir=curdir;
2800: tp=0;
2801: if(!strncmp(p,"...",3)) {
2802: curlib=-1;
2803: curdir=-1;
2804: p+=3;
2805: }
2806: if(!strncmp(p,"./",2))
2807: p+=2;
2808: else if(!strncmp(p,"..",2)) {
2809: if(curdir<0)
2810: curlib=-1;
2811: else
2812: curdir=-1;
2813: p+=2;
2814: }
2815: if(*p==0)
2816: success=TRUE;
2817: else if(!strcmp(p,"."))
2818: success=TRUE;
2819: if(!success && (curlib<0 || *p=='/')) { /* Root dir */
2820: if(*p=='/') p++;
2821: tp=strchr(p,'/');
2822: if(tp) *tp=0;
2823: for(i=0;i<scfg.total_libs;i++) {
2824: if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
2825: continue;
2826: if(!stricmp(scfg.lib[i]->sname,p))
2827: break;
2828: }
2829: if(i<scfg.total_libs) {
2830: curlib=i;
2831: success=TRUE;
2832: }
2833: }
2834: if((!success && curdir<0) || (success && tp && *(tp+1))) {
2835: if(tp)
2836: p=tp+1;
2837: tp=strchr(p,'/');
2838: if(tp) *tp=0;
2839: for(i=0;i<scfg.total_dirs;i++) {
2840: if(scfg.dir[i]->lib!=curlib)
2841: continue;
2842: if(i!=scfg.sysop_dir && i!=scfg.upload_dir
2843: && !chk_ar(&scfg,scfg.dir[i]->ar,&user))
2844: continue;
2845: if(!stricmp(scfg.dir[i]->code,p))
2846: break;
2847: }
2848: if(i<scfg.total_dirs) {
2849: curdir=i;
2850: success=TRUE;
2851: } else
2852: success=FALSE;
2853: }
2854:
2855: if(success)
2856: sockprintf(sock,"250 CWD command successful.");
2857: else {
2858: sockprintf(sock,"550 %s: No such file or directory.",p);
2859: curlib=orglib;
2860: curdir=orgdir;
2861: }
2862: continue;
2863: }
2864:
2865: if(!stricmp(cmd, "PWD") || !stricmp(cmd,"XPWD")) {
2866: if(curlib<0)
2867: sockprintf(sock,"257 \"/\" is current directory.");
2868: else if(curdir<0)
2869: sockprintf(sock,"257 \"/%s\" is current directory."
2870: ,scfg.lib[curlib]->sname);
2871: else
2872: sockprintf(sock,"257 \"/%s/%s\" is current directory."
2873: ,scfg.lib[curlib]->sname,scfg.dir[curdir]->code);
2874: continue;
2875: }
2876:
2877: sockprintf(sock,"500 Syntax error: '%s'",cmd);
2878: lprintf("%04d !FTP: UNSUPPORTED COMMAND: '%s'",sock,cmd);
2879: }
2880:
2881: if(transfer_inprogress==TRUE) {
2882: lprintf("%04d Waiting for transfer to complete...",sock);
2883: while(data_sock!=INVALID_SOCKET && transfer_inprogress==TRUE) {
2884: mswait(500);
2885: if(gettimeleft(&scfg,&user,logintime)<1) {
2886: lprintf("%04d Out of time, disconnecting",sock);
2887: sockprintf(sock,"421 Sorry, you've run out of time.");
2888: close_socket(&data_sock,__LINE__);
2889: transfer_aborted=TRUE;
2890: }
2891: if((time(NULL)-lastactive)>startup->max_inactivity) {
2892: lprintf("%04d Disconnecting due to to inactivity.",sock);
2893: sockprintf(sock,"421 Disconnecting due to inactivity (%u seconds)."
2894: ,startup->max_inactivity);
2895: close_socket(&data_sock,__LINE__);
2896: transfer_aborted=TRUE;
2897: }
2898: }
2899: lprintf("%04d Done waiting for transfer to complete",sock);
2900: }
2901:
2902: /* Update User Statistics */
2903: if(user.number) {
2904: user.tlast=(ushort)(time(NULL)-logintime);
2905: putuserrec(&scfg,user.number,U_LASTON,8,ultoa(time(NULL),str,16));
2906: putuserrec(&scfg,user.number,U_TLAST,5,ultoa(user.tlast,str,10));
2907: adjustuserrec(&scfg,user.number,U_TIMEON,5,user.tlast);
2908: }
2909:
2910: if(user.number)
2911: lprintf("%04d %s logged off.",sock,user.alias);
2912:
2913: #ifdef _WIN32
2914: if(startup->hangup_sound[0] && !(startup->options&FTP_OPT_MUTE))
2915: PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME);
2916: #endif
2917:
2918: status(STATUS_WFC);
2919:
2920: lprintf("%04d CTRL thread terminated", sock);
2921: active_clients--;
2922: update_clients();
2923: client_off(sock);
2924:
2925: /* Free up resources here */
2926: close_socket(&sock,__LINE__);
2927: if(pasv_sock!=INVALID_SOCKET)
2928: close_socket(&pasv_sock,__LINE__);
2929: if(data_sock!=INVALID_SOCKET)
2930: close_socket(&data_sock,__LINE__);
2931:
2932: thread_down();
2933: }
2934:
2935: static void cleanup(int code)
2936: {
2937: if(server_socket!=INVALID_SOCKET)
2938: close_socket(&server_socket,__LINE__);
2939: server_socket=INVALID_SOCKET;
2940:
2941: update_clients();
2942:
2943: #ifdef _WINSOCKAPI_
2944: if(WSAInitialized && WSACleanup()!=0)
2945: lprintf("0000 !WSACleanup ERROR %d",ERROR_VALUE);
2946: #endif
2947:
2948: lprintf("#### FTP Server thread terminated");
2949: status("Down");
2950: if(startup!=NULL && startup->terminated!=NULL)
2951: startup->terminated(code);
2952: thread_down();
2953: }
2954:
2955: char* DLLCALL ftp_ver(void)
2956: {
2957: static char ver[256];
2958: char compiler[32];
2959:
2960: COMPILER_DESC(compiler);
2961:
2962: sprintf(ver,"Synchronet FTP Server v%s%s "
2963: "Compiled %s %s with %s"
2964: ,FTP_VERSION
2965: #ifdef _DEBUG
2966: ," Debug"
2967: #else
2968: ,""
2969: #endif
2970: ,__DATE__, __TIME__, compiler);
2971:
2972: return(ver);
2973: }
2974:
2975: void DLLCALL ftp_server(void* arg)
2976: {
2977: char compiler[32];
2978: SOCKADDR_IN server_addr;
2979: SOCKADDR_IN client_addr;
2980: int client_addr_len;
2981: SOCKET client_socket;
2982: int i;
2983: int result;
2984: time_t t;
2985: time_t start;
2986: LINGER linger;
2987: fd_set socket_set;
2988: SOCKET high_socket_set;
2989: ftp_t* ftp;
2990: struct timeval tv;
2991:
2992: startup=(ftp_startup_t*)arg;
2993:
2994: if(startup==NULL) {
2995: sbbs_beep(100,500);
2996: fprintf(stderr, "No startup structure passed!\n");
2997: return;
2998: }
2999:
3000: if(startup->size!=sizeof(ftp_startup_t)) { /* verify size */
3001: sbbs_beep(100,500);
3002: sbbs_beep(300,500);
3003: sbbs_beep(100,500);
3004: fprintf(stderr, "Invalid startup structure!\n");
3005: return;
3006: }
3007:
3008: thread_up();
3009:
3010: status("Initializing");
3011:
3012: #ifdef __unix__ /* Ignore "Broken Pipe" signal */
3013: signal(SIGPIPE,SIG_IGN);
3014: #endif
3015:
3016: lprintf("Synchronet FTP Server Version %s%s"
3017: ,FTP_VERSION
3018: #ifdef _DEBUG
3019: ," Debug"
3020: #else
3021: ,""
3022: #endif
3023: );
3024:
3025: COMPILER_DESC(compiler);
3026:
3027: lprintf("Compiled %s %s with %s", __DATE__, __TIME__, compiler);
3028:
3029: srand(time(NULL));
3030:
3031: if(!(startup->options&FTP_OPT_LOCAL_TIMEZONE)) {
3032: if(PUTENV("TZ=UCT0"))
3033: lprintf("!putenv() FAILED");
3034: tzset();
3035:
3036: if((t=checktime())!=0) { /* Check binary time */
3037: lprintf("!TIME PROBLEM (%ld)",t);
3038: cleanup(1);
3039: return;
3040: }
3041: }
3042:
3043: if(!winsock_startup()) {
3044: cleanup(1);
3045: return;
3046: }
3047:
3048: t=time(NULL);
3049: lprintf("Initializing on %.24s with options: %lx"
3050: ,ctime(&t),startup->options);
3051:
3052: #ifdef _WIN32
3053: if((socket_mutex=CreateMutex(NULL,FALSE,NULL))==NULL) {
3054: lprintf("!ERROR %d creating socket_mutex", GetLastError());
3055: cleanup(1);
3056: return;
3057: }
3058: #endif
3059:
3060: /* Initial configuration and load from CNF files */
3061: memset(&scfg, 0, sizeof(scfg));
3062: sprintf(scfg.ctrl_dir, "%.*s",(int)sizeof(scfg.ctrl_dir)-1
3063: ,startup->ctrl_dir);
3064: lprintf("Loading configuration files from %s", scfg.ctrl_dir);
3065: if(!load_cfg(&scfg, NULL)) {
3066: lprintf("!Failed to load configuration files");
3067: cleanup(1);
3068: return;
3069: }
3070:
3071: if(!startup->max_clients) {
3072: startup->max_clients=scfg.sys_nodes;
3073: if(startup->max_clients<10)
3074: startup->max_clients=10;
3075: }
3076: lprintf("Maximum clients: %d",startup->max_clients);
3077:
3078: if(!startup->max_inactivity)
3079: startup->max_inactivity=300; /* seconds */
3080:
3081: lprintf("Maximum inactivity: %d seconds",startup->max_inactivity);
3082:
3083: active_clients=0;
3084: update_clients();
3085:
3086: strlwr(scfg.sys_id); /* Use lower-case unix-looking System ID for group name */
3087:
3088: for(i=0;i<scfg.total_libs;i++) {
3089: strlwr(scfg.lib[i]->sname);
3090: dotname(scfg.lib[i]->sname,scfg.lib[i]->sname);
3091: }
3092:
3093: for(i=0;i<scfg.total_dirs;i++)
3094: strlwr(scfg.dir[i]->code);
3095:
3096:
3097: /* open a socket and wait for a client */
3098:
3099: if((server_socket=open_socket(SOCK_STREAM))==INVALID_SOCKET) {
3100: lprintf("!ERROR %d opening socket", ERROR_VALUE);
3101: cleanup(1);
3102: return;
3103: }
3104:
3105: lprintf("%04d FTP socket opened",server_socket);
3106:
3107: #if 1
3108: linger.l_onoff=TRUE;
3109: linger.l_linger=5; /* seconds */
3110:
3111: if((result=setsockopt(server_socket, SOL_SOCKET, SO_LINGER
3112: ,(char *)&linger, sizeof(linger)))!=0) {
3113: lprintf ("%04d !ERROR %d (%d) setting socket options."
3114: ,server_socket, result, ERROR_VALUE);
3115: cleanup(1);
3116: return;
3117: }
3118: #endif
3119:
3120: /*****************************/
3121: /* Listen for incoming calls */
3122: /*****************************/
3123: memset(&server_addr, 0, sizeof(server_addr));
3124:
3125: server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
3126: server_addr.sin_family = AF_INET;
3127: server_addr.sin_port = htons(startup->port);
3128:
3129: if((result=bind(server_socket, (struct sockaddr *) &server_addr
3130: ,sizeof(server_addr)))!=0) {
3131: lprintf("%04d !ERROR %d (%d) binding socket to port %d"
3132: ,server_socket, result, ERROR_VALUE,startup->port);
3133: cleanup(1);
3134: return;
3135: }
3136:
3137: if((result=listen(server_socket, 1))!= 0) {
3138: lprintf("%04d !ERROR %d (%d) listening on socket"
3139: ,server_socket, result, ERROR_VALUE);
3140: cleanup(1);
3141: return;
3142: }
3143:
3144: /* signal caller that we've started up successfully */
3145: if(startup->started!=NULL)
3146: startup->started();
3147:
3148: lprintf("%04d FTP Server thread started on port %d",server_socket,startup->port);
3149: status(STATUS_WFC);
3150:
3151: while(server_socket!=INVALID_SOCKET) {
3152:
3153: /* now wait for connection */
3154:
3155: tv.tv_sec=2;
3156: tv.tv_usec=0;
3157:
3158: FD_ZERO(&socket_set);
3159: FD_SET(server_socket,&socket_set);
3160: high_socket_set=server_socket+1;
3161:
3162: if((i=select(high_socket_set,&socket_set,NULL,NULL,&tv))<1) {
3163: if(i==0) {
3164: mswait(1);
3165: continue;
3166: }
3167: if(ERROR_VALUE==EINTR)
3168: lprintf("0000 FTP Server listening interrupted");
3169: else if(ERROR_VALUE == ENOTSOCK)
3170: lprintf("0000 FTP Server sockets closed");
3171: else
3172: lprintf("0000 !ERROR %d selecting sockets",ERROR_VALUE);
3173: break;
3174: }
3175:
3176: client_addr_len = sizeof(client_addr);
3177: client_socket = accept(server_socket, (struct sockaddr *)&client_addr
3178: ,&client_addr_len);
3179:
3180: if(client_socket == INVALID_SOCKET)
3181: {
3182: if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINTR)
3183: lprintf("0000 FTP socket closed while listening");
3184: else
3185: lprintf("0000 !accept failed (ERROR %d)", ERROR_VALUE);
3186: break;
3187: }
3188: if(startup->socket_open!=NULL)
3189: startup->socket_open(TRUE);
3190: sockets++;
3191:
3192: if(active_clients>=startup->max_clients) {
3193: lprintf("%04d !MAXMIMUM CLIENTS (%d) reached, access denied"
3194: ,client_socket, startup->max_clients);
3195: sockprintf(client_socket,"421 Maximum active clients reached, please try again later.");
3196: mswait(3000);
3197: close_socket(&client_socket,__LINE__);
3198: continue;
3199: }
3200:
3201: if((ftp=malloc(sizeof(ftp_t)))==NULL) {
3202: lprintf("%04d !ERROR allocating %d bytes of memory for ftp_t"
3203: ,client_socket,sizeof(ftp_t));
3204: sockprintf(client_socket,"421 System error, please try again later.");
3205: mswait(3000);
3206: close_socket(&client_socket,__LINE__);
3207: continue;
3208: }
3209:
3210: ftp->socket=client_socket;
3211: ftp->client_addr=client_addr;
3212:
3213: _beginthread (ctrl_thread, 0, ftp);
3214: }
3215:
3216: if(active_clients) {
3217: lprintf("0000 Waiting for %d active clients to disconnect...", active_clients);
3218: start=time(NULL);
3219: while(active_clients) {
3220: if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
3221: lprintf("0000 !TIMEOUT waiting for %d active clients ",active_clients);
3222: break;
3223: }
3224: mswait(100);
3225: }
3226: }
3227:
3228: cleanup(0);
3229: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.