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