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