|
|
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.