Newer
Older
/* ftpsrvr.c */
/* Synchronet FTP server */
/* $Id$ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 2010 Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* Anonymous FTP access to the most recent released source is available at *
* ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
* *
* Anonymous CVS access to the development source and modification history *
* is available at cvs.synchro.net:/cvsroot/sbbs, example: *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login *
* (just hit return, no password is necessary) *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* You are encouraged to submit any modifications (preferably in Unix diff *
* format) via e-mail to mods@synchro.net *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include <stdlib.h> /* ltoa in GNU C lib */
#include <stdarg.h> /* va_list, varargs */
#include <string.h> /* strrchr */
#include <fcntl.h> /* O_WRONLY, O_RDONLY, etc. */
#include <errno.h> /* EACCES */
#include <ctype.h> /* toupper */
#include <sys/stat.h> /* S_IWRITE */
#undef SBBS /* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
#include "sbbs.h"
#include "text.h" /* TOTAL_TEXT */
#include "js_rtpool.h"
#include "js_request.h"
#define FTP_SERVER "Synchronet FTP Server"
#define STATUS_WFC "Listening"
#define ANONYMOUS "anonymous"
#define BBS_VIRTUAL_PATH "bbs:/""/" /* this is actually bbs:<slash><slash> */
#define LOCAL_FSYS_DIR "local:"
#define BBS_FSYS_DIR "bbs:"
#define BBS_HIDDEN_ALIAS "hidden"
#define TIMEOUT_THREAD_WAIT 60 /* Seconds */
#define TIMEOUT_SOCKET_LISTEN 30 /* Seconds */
#define XFER_REPORT_INTERVAL 60 /* Seconds */
#define INDEX_FNAME_LEN 15
#define NAME_LEN 15 /* User name length for listings */
static ftp_startup_t* startup=NULL;
static scfg_t scfg;
static SOCKET server_socket=INVALID_SOCKET;
static ulong active_clients=0;
static ulong sockets=0;
static ulong thread_count=0;
static time_t uptime=0;
static BOOL terminate_server=FALSE;
static char revision[16];
static char *text[TOTAL_TEXT];
static str_list_t recycle_semfiles;
static str_list_t shutdown_semfiles;
#ifdef SOCKET_DEBUG
static BYTE socket_debug[0x10000]={0};
#define SOCKET_DEBUG_CTRL (1<<0) /* 0x01 */
#define SOCKET_DEBUG_SEND (1<<1) /* 0x02 */
#define SOCKET_DEBUG_READLINE (1<<2) /* 0x04 */
#define SOCKET_DEBUG_ACCEPT (1<<3) /* 0x08 */
#define SOCKET_DEBUG_SENDTHREAD (1<<4) /* 0x10 */
#define SOCKET_DEBUG_TERMINATE (1<<5) /* 0x20 */
#define SOCKET_DEBUG_RECV_CHAR (1<<6) /* 0x40 */
#define SOCKET_DEBUG_FILEXFER (1<<7) /* 0x80 */
char* genvpath(int lib, int dir, char* str);
typedef struct {
SOCKET socket;
SOCKADDR_IN client_addr;
} ftp_t;
static const char *ftp_mon[]={"Jan","Feb","Mar","Apr","May","Jun"
,"Jul","Aug","Sep","Oct","Nov","Dec"};
BOOL direxist(char *dir)
{
if(access(dir,0)==0)
return(TRUE);
else
return(FALSE);
}
BOOL dir_op(scfg_t* cfg, user_t* user, client_t* client, uint dirnum)
{
return(user->level>=SYSOP_LEVEL || user->exempt&FLAG('R')
|| (cfg->dir[dirnum]->op_ar[0] && chk_ar(cfg,cfg->dir[dirnum]->op_ar,user,client)));
static int lprintf(int level, const char *fmt, ...)
{
va_list argptr;
char sbuf[1024];
va_start(argptr,fmt);
vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
sbuf[sizeof(sbuf)-1]=0;
if(level <= LOG_ERR) {
errorlog(&scfg, startup==NULL ? NULL:startup->host_name, sbuf);
if(startup!=NULL && startup->errormsg!=NULL)
startup->errormsg(startup->cbdata,level,sbuf);
}
if(startup==NULL || startup->lputs==NULL || level > startup->log_level)
return(0);
#if defined(_WIN32)
if(IsBadCodePtr((FARPROC)startup->lputs))
return(0);
#endif
return startup->lputs(startup->cbdata,level,sbuf);
}
#ifdef _WINSOCKAPI_
static WSADATA WSAData;
#define SOCKLIB_DESC WSAData.szDescription
static BOOL WSAInitialized=FALSE;
static BOOL winsock_startup(void)
{
int status; /* Status Code */
if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
WSAInitialized=TRUE;
lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
#else /* No WINSOCK */
#define winsock_startup() (TRUE)
#define SOCKLIB_DESC NULL
#endif
static void status(char* str)
{
if(startup!=NULL && startup->status!=NULL)
startup->status(startup->cbdata,str);
}
static void update_clients(void)
{
if(startup!=NULL && startup->clients!=NULL)
startup->clients(startup->cbdata,active_clients);
static void client_on(SOCKET sock, client_t* client, BOOL update)
{
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(startup->cbdata,TRUE,sock,client,update);
}
static void client_off(SOCKET sock)
{
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
static void thread_up(BOOL setuid)
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,TRUE, setuid);
}
static void thread_down(void)
{
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,FALSE, FALSE);

rswindell
committed
static SOCKET ftp_open_socket(int type)
SOCKET sock;
char error[256];
sock=socket(AF_INET, type, IPPROTO_IP);
if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,TRUE);
if(set_socket_options(&scfg, sock, "FTP", error, sizeof(error)))
lprintf(LOG_ERR,"%04d !ERROR %s",sock, error);
#ifdef SOCKET_DEBUG
lprintf(LOG_DEBUG,"%04d Socket opened (%u sockets in use)",sock,sockets);
#endif
}
return(sock);
}
#ifdef __BORLANDC__
#pragma argsused
#endif

rswindell
committed
static int ftp_close_socket(SOCKET* sock, int line)
{
int result;
if((*sock)==INVALID_SOCKET) {
lprintf(LOG_WARNING,"0000 !INVALID_SOCKET in close_socket from line %u",line);
shutdown(*sock,SHUT_RDWR); /* required on Unix */
if(startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,FALSE);

rswindell
committed
if(result!=0) {
if(ERROR_VALUE!=ENOTSOCK)
lprintf(LOG_WARNING,"%04d !ERROR %d closing socket from line %u",*sock,ERROR_VALUE,line);
} else if(sock==&server_socket || *sock==server_socket)
lprintf(LOG_DEBUG,"%04d Server socket closed (%u sockets in use) from line %u",*sock,sockets,line);
#ifdef SOCKET_DEBUG

rswindell
committed
else
lprintf(LOG_DEBUG,"%04d Socket closed (%u sockets in use) from line %u",*sock,sockets,line);
#endif
*sock=INVALID_SOCKET;
return(result);
}
static int sockprintf(SOCKET sock, char *fmt, ...)
{
int len;
int maxlen;
int result;
va_list argptr;
char sbuf[1024];
fd_set socket_set;
struct timeval tv;
len=vsnprintf(sbuf,maxlen=sizeof(sbuf)-2,fmt,argptr);
va_end(argptr);
if(len<0 || len>maxlen) /* format error or output truncated */
len=maxlen;
if(startup!=NULL && startup->options&FTP_OPT_DEBUG_TX)
lprintf(LOG_DEBUG,"%04d TX: %.*s", sock, len, sbuf);
memcpy(sbuf+len,"\r\n",2);
if(sock==INVALID_SOCKET) {
lprintf(LOG_WARNING,"!INVALID SOCKET in call to sockprintf");
return(0);
}
/* Check socket for writability (using select) */
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(sock,&socket_set);
if((result=select(sock+1,NULL,&socket_set,NULL,&tv))<1) {
lprintf(LOG_WARNING,"%04d !TIMEOUT selecting socket for send"
lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket for send"
,sock, ERROR_VALUE);
while((result=sendsocket(sock,sbuf,len))!=len) {
YIELD();
lprintf(LOG_WARNING,"%04d Connection reset by peer on send",sock);
lprintf(LOG_WARNING,"%04d Connection aborted by peer on send",sock);
lprintf(LOG_WARNING,"%04d !ERROR %d sending",sock,ERROR_VALUE);
lprintf(LOG_WARNING,"%04d !ERROR: short send: %u instead of %u",sock,result,len);
}
return(len);
}
/* Returns the directory index of a virtual lib/dir path (e.g. main/games/filename) */
{
char* tp;
char path[MAX_PATH+1];
p=path;
if(*p=='/')
p++;
else if(!strncmp(p,"./",2))
p+=2;
tp=strchr(p,'/');
if(tp) *tp=0;
for(lib=0;lib<scfg.total_libs;lib++) {
continue;
if(!stricmp(scfg.lib[lib]->sname,p))
break;
}
if(lib>=scfg.total_libs)
return(-1);
if(tp!=NULL)
p=tp+1;
tp=strchr(p,'/');
if(tp) *tp=0;
for(dir=0;dir<scfg.total_dirs;dir++) {
if(scfg.dir[dir]->lib!=lib)
continue;
if(dir!=scfg.sysop_dir && dir!=scfg.upload_dir
if(!stricmp(scfg.dir[dir]->code_suffix,p))
break;
}
if(dir>=scfg.total_dirs)
return(-1);
return(dir);
}
/*********************************/
/* JavaScript Data and Functions */
/*********************************/
#ifdef JAVASCRIPT
js_server_props_t js_server_props;
static JSBool
js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
uintN i;
JSString* str=NULL;
jsrefcount rc;
if((fp=(FILE*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
for (i = 0; i < argc; i++) {
str = JS_ValueToString(cx, argv[i]);
if (!str)
return JS_FALSE;
rc=JS_SUSPENDREQUEST(cx);
fprintf(fp,"%s",JS_GetStringBytes(str));
JS_RESUMEREQUEST(cx, rc);
if(str==NULL)
*rval = JSVAL_VOID;
else
*rval = STRING_TO_JSVAL(str);
return(JS_TRUE);
}
static JSBool
js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
FILE* fp;
jsrefcount rc;
if((fp=(FILE*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
js_write(cx,obj,argc,argv,rval);
rc=JS_SUSPENDREQUEST(cx);
fprintf(fp,"\r\n");
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
static JSFunctionSpec js_global_functions[] = {
{"write", js_write, 1}, /* write to HTML file */
{"writeln", js_writeln, 1}, /* write to HTML file */
{"print", js_writeln, 1}, /* alias for writeln */
{0}
};
static void
js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
char line[64];
char file[MAX_PATH+1];
char* warning;
int log_level;
lprintf(LOG_ERR,"!JavaScript: %s", message);
if(fp!=NULL)
fprintf(fp,"!JavaScript: %s", message);
return;
}
if(report->filename)
sprintf(file," %s",report->filename);
else
file[0]=0;
if(report->lineno)
sprintf(line," line %u",report->lineno);
else
line[0]=0;
if(JSREPORT_IS_WARNING(report->flags)) {
if(JSREPORT_IS_STRICT(report->flags))
warning="strict warning";
else
warning="warning";
log_level=LOG_WARNING;
} else {
log_level=LOG_ERR;
lprintf(log_level,"!JavaScript %s%s%s: %s",warning,file,line,message);
if(fp!=NULL)
fprintf(fp,"!JavaScript %s%s%s: %s",warning,file,line,message);
js_initcx(JSRuntime* runtime, SOCKET sock, JSObject** glob, JSObject** ftp, js_branch_t* branch)
{
JSContext* js_cx;
JSObject* js_glob;
lprintf(LOG_DEBUG,"%04d JavaScript: Initializing context (stack: %lu bytes)"
,sock,startup->js.cx_stack);
if((js_cx = JS_NewContext(runtime, startup->js.cx_stack))==NULL)
JS_BEGINREQUEST(js_cx);
lprintf(LOG_DEBUG,"%04d JavaScript: Context created",sock);
JS_SetErrorReporter(js_cx, js_ErrorReporter);
memset(branch, 0, sizeof(js_branch_t));
/* ToDo: call js_CreateCommonObjects() instead */
lprintf(LOG_DEBUG,"%04d JavaScript: Initializing Global object",sock);
if((js_glob=js_CreateGlobalObject(js_cx, &scfg, NULL, &startup->js))==NULL)
if(!JS_DefineFunctions(js_cx, js_glob, js_global_functions))
break;
/* Internal JS Object */
if(js_CreateInternalJsObject(js_cx, js_glob, branch, &startup->js)==NULL)
lprintf(LOG_DEBUG,"%04d JavaScript: Initializing System object",sock);
if(js_CreateSystemObject(js_cx, js_glob, &scfg, uptime, startup->host_name, SOCKLIB_DESC)==NULL)
break;
if((*ftp=JS_DefineObject(js_cx, js_glob, "ftp", NULL
,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))==NULL)
break;
if(js_CreateServerObject(js_cx,js_glob,&js_server_props)==NULL)
break;
if(glob!=NULL)
*glob=js_glob;
success=TRUE;
} while(0);
JS_ENDREQUEST(js_cx);
JS_DestroyContext(js_cx);
return(NULL);
}
return(js_cx);
}
BOOL js_add_file(JSContext* js_cx, JSObject* array,
char* name, char* desc, char* ext_desc,
ulong size, ulong credits,
time_t time, time_t uploaded, time_t last_downloaded,
ulong times_downloaded, ulong misc,
char* uploader, char* link)

rswindell
committed
jsuint index;
if((file=JS_NewObject(js_cx, NULL, NULL, NULL))==NULL)
if((js_str=JS_NewStringCopyZ(js_cx, name))==NULL)
return(FALSE);
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(js_cx, file, "name", &val))
return(FALSE);
if((js_str=JS_NewStringCopyZ(js_cx, desc))==NULL)
return(FALSE);
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(js_cx, file, "description", &val))
return(FALSE);
if((js_str=JS_NewStringCopyZ(js_cx, ext_desc))==NULL)
return(FALSE);
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(js_cx, file, "extended_description", &val))
return(FALSE);
val=INT_TO_JSVAL(size);
if(!JS_SetProperty(js_cx, file, "size", &val))
return(FALSE);
val=INT_TO_JSVAL(credits);
if(!JS_SetProperty(js_cx, file, "credits", &val))
return(FALSE);
JS_NewNumberValue(js_cx,time,&val);
if(!JS_SetProperty(js_cx, file, "time", &val))
return(FALSE);
val=INT_TO_JSVAL(uploaded);
if(!JS_SetProperty(js_cx, file, "uploaded", &val))
return(FALSE);
val=INT_TO_JSVAL(last_downloaded);
if(!JS_SetProperty(js_cx, file, "last_downloaded", &val))
return(FALSE);
val=INT_TO_JSVAL(times_downloaded);
if(!JS_SetProperty(js_cx, file, "times_downloaded", &val))
return(FALSE);
if((js_str=JS_NewStringCopyZ(js_cx, uploader))==NULL)
return(FALSE);
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(js_cx, file, "uploader", &val))
return(FALSE);
val=INT_TO_JSVAL(misc);
if(!JS_SetProperty(js_cx, file, "settings", &val))
return(FALSE);
if((js_str=JS_NewStringCopyZ(js_cx, link))==NULL)
return(FALSE);
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(js_cx, file, "link", &val))
return(FALSE);
if(!JS_GetArrayLength(js_cx, array, &index))
return(FALSE);
val=OBJECT_TO_JSVAL(file);
return(JS_SetElement(js_cx, array, index, &val));
}
BOOL js_generate_index(JSContext* js_cx, JSObject* parent,
SOCKET sock, FILE* fp, int lib, int dir, user_t* user, client_t* client)
{
char str[256];
char path[MAX_PATH+1];
char spath[MAX_PATH+1]; /* script path */
char vpath[MAX_PATH+1]; /* virtual path */
char aliasfile[MAX_PATH+1];
char extdesc[513];
BOOL alias_dir;
BOOL success=FALSE;
FILE* alias_fp;
uint i;
file_t f;
glob_t g;
jsval val;
jsval rval;
JSObject* lib_obj=NULL;
JSObject* dir_obj=NULL;
JSObject* file_array=NULL;
JSObject* dir_array=NULL;
JSScript* js_script=NULL;
long double start=xp_timer();
jsrefcount rc;
lprintf(LOG_DEBUG,"%04d JavaScript: Generating HTML Index for %s"
,sock, genvpath(lib,dir,str));
JS_SetContextPrivate(js_cx, fp);
do { /* pseudo try/catch */
if((file_array=JS_NewArrayObject(js_cx, 0, NULL))==NULL) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to create file_array",sock);
/* file[] */
val=OBJECT_TO_JSVAL(file_array);
if(!JS_SetProperty(js_cx, parent, "file_list", &val)) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to set file property",sock);
break;
}
if((dir_array=JS_NewArrayObject(js_cx, 0, NULL))==NULL) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to create dir_array",sock);
/* dir[] */
val=OBJECT_TO_JSVAL(dir_array);
if(!JS_SetProperty(js_cx, parent, "dir_list", &val)) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to set dir property",sock);
break;
}
rc=JS_SUSPENDREQUEST(js_cx);
if(strcspn(startup->html_index_script,"/\\")==strlen(startup->html_index_script)) {
sprintf(spath,"%s%s",scfg.mods_dir,startup->html_index_script);
if(scfg.mods_dir[0]==0 || !fexist(spath))
sprintf(spath,"%s%s",scfg.exec_dir,startup->html_index_script);
} else
sprintf(spath,"%.*s",(int)sizeof(spath)-4,startup->html_index_script);
/* Add extension if not specified */
if(!strchr(spath,'.'))
strcat(spath,".js");
if(!fexist(spath)) {
JS_RESUMEREQUEST(js_cx, rc);
lprintf(LOG_ERR,"%04d !HTML JavaScript (%s) doesn't exist",sock,spath);
JS_RESUMEREQUEST(js_cx, rc);
if((js_str=JS_NewStringCopyZ(js_cx, startup->html_index_file))==NULL)
break;
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(js_cx, parent, "html_index_file", &val)) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to set html_index_file property",sock);
break;
}
/* curlib */
if((lib_obj=JS_NewObject(js_cx, NULL, 0, NULL))==NULL) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to create lib_obj",sock);
break;
}
val=OBJECT_TO_JSVAL(lib_obj);
if(!JS_SetProperty(js_cx, parent, "curlib", &val)) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curlib property",sock);
/* curdir */
if((dir_obj=JS_NewObject(js_cx, NULL, 0, NULL))==NULL) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to create dir_obj",sock);
val=OBJECT_TO_JSVAL(dir_obj);
if(!JS_SetProperty(js_cx, parent, "curdir", &val)) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir property",sock);
if(lib>=0) { /* root */
strcat(vpath,scfg.lib[lib]->sname);
if((js_str=JS_NewStringCopyZ(js_cx, scfg.lib[lib]->sname))==NULL)
break;
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(js_cx, lib_obj, "name", &val)) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curlib.name property",sock);
break;
}
if((js_str=JS_NewStringCopyZ(js_cx, scfg.lib[lib]->lname))==NULL)
break;
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(js_cx, lib_obj, "description", &val)) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curlib.desc property",sock);
break;
}
if(dir>=0) { /* 1st level */
strcat(vpath,scfg.dir[dir]->code_suffix);
strcat(vpath,"/");
if((js_str=JS_NewStringCopyZ(js_cx, scfg.dir[dir]->code))==NULL)
break;
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(js_cx, dir_obj, "code", &val)) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir.code property",sock);
break;
}
if((js_str=JS_NewStringCopyZ(js_cx, scfg.dir[dir]->sname))==NULL)
break;
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(js_cx, dir_obj, "name", &val)) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir.name property",sock);
break;
}
if((js_str=JS_NewStringCopyZ(js_cx, scfg.dir[dir]->lname))==NULL)
break;
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(js_cx, dir_obj, "description", &val)) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir.desc property",sock);
break;
}
val=INT_TO_JSVAL(scfg.dir[dir]->misc);
if(!JS_SetProperty(js_cx, dir_obj, "settings", &val)) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir.misc property",sock);
break;
}
if((js_str=JS_NewStringCopyZ(js_cx, vpath))==NULL)
break;
val=STRING_TO_JSVAL(js_str);
if(!JS_SetProperty(js_cx, parent, "path", &val)) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to set curdir property",sock);
break;
}
if(lib<0) { /* root dir */
rc=JS_SUSPENDREQUEST(js_cx);
/* File Aliases */
sprintf(path,"%sftpalias.cfg",scfg.ctrl_dir);
if((alias_fp=fopen(path,"r"))!=NULL) {
while(!feof(alias_fp)) {
if(!fgets(aliasline,sizeof(aliasline),alias_fp))
break;
p=aliasline; /* alias pointer */
SKIP_WHITESPACE(p);
if(*p==';') /* comment */
continue;
tp=p; /* terminator pointer */
FIND_WHITESPACE(tp);
if(*tp) *tp=0;
np=tp+1; /* filename pointer */
SKIP_WHITESPACE(np);
tp=np; /* terminator pointer */
FIND_WHITESPACE(tp);
if(*tp) *tp=0;
dp=tp+1; /* description pointer */
SKIP_WHITESPACE(dp);
truncsp(dp);
if(stricmp(dp,BBS_HIDDEN_ALIAS)==0)
continue;
alias_dir=FALSE;
/* Virtual Path? */
if(!strnicmp(np,BBS_VIRTUAL_PATH,strlen(BBS_VIRTUAL_PATH))) {
if((dir=getdir(np+strlen(BBS_VIRTUAL_PATH),user,client))<0)
continue; /* No access or invalid virtual path */
tp=strrchr(np,'/');
if(tp==NULL)
continue;
tp++;
if(*tp) {
SAFEPRINTF2(aliasfile,"%s%s",scfg.dir[dir]->path,tp);
np=aliasfile;
}
else
alias_dir=TRUE;
}
if(!alias_dir && !fexist(np))
continue;
SAFEPRINTF2(vpath,"/%s/%s",p,startup->html_index_file);
JS_RESUMEREQUEST(js_cx, rc);
,alias_dir ? dir_array : file_array
,dp /* description */
,NULL /* extdesc */
,flength(np) /* size */
,fdate(np) /* time */
,fdate(np) /* uploaded */
,0 /* last downloaded */
,0 /* times downloaded */
,0 /* misc */
,vpath /* link */
rc=JS_SUSPENDREQUEST(js_cx);
JS_RESUMEREQUEST(js_cx, rc);
/* QWK Packet */
if(startup->options&FTP_OPT_ALLOW_QWK /* && fexist(qwkfile) */) {
sprintf(str,"%s.qwk",scfg.sys_id);
SAFEPRINTF(vpath,"/%s",str);
js_add_file(js_cx
,file_array
,str /* filename */
,"QWK Message Packet" /* description */
,NULL /* extdesc */
,time(NULL) /* time */
,0 /* uploaded */
,0 /* last downloaded */
,0 /* times downloaded */
,0 /* misc */
,str /* link */
);
}
/* Library Folders */
for(i=0;i<scfg.total_libs;i++) {
SAFEPRINTF2(vpath,"/%s/%s",scfg.lib[i]->sname,startup->html_index_file);
js_add_file(js_cx
,dir_array
,scfg.lib[i]->sname /* filename */
,scfg.lib[i]->lname /* description */
,NULL /* extdesc */
,0,0,0,0,0,0,0 /* unused */
,vpath /* link */
for(i=0;i<scfg.total_dirs;i++) {
if(scfg.dir[i]->lib!=lib)
continue;
if(/* i!=scfg.sysop_dir && i!=scfg.upload_dir && */
SAFEPRINTF3(vpath,"/%s/%s/%s"
,scfg.dir[i]->code_suffix
,startup->html_index_file);
js_add_file(js_cx
,dir_array
,scfg.dir[i]->sname /* filename */
,NULL /* extdesc */
,getfiles(&scfg,i) /* size */
,0,0,0,0,0 /* unused */
,scfg.dir[i]->misc /* misc */
,vpath /* link */
SAFEPRINTF(path,"%s*",scfg.dir[dir]->path);
rc=JS_SUSPENDREQUEST(js_cx);
glob(path,0,NULL,&g);
for(i=0;i<(int)g.gl_pathc;i++) {
if(isdir(g.gl_pathv[i]))
continue;
#ifdef _WIN32
GetShortPathName(g.gl_pathv[i], str, sizeof(str));
#else
#endif
padfname(getfname(str),f.name);
f.dir=dir;
if(getfileixb(&scfg,&f)) {
f.size=0; /* flength(g.gl_pathv[i]); */
getfiledat(&scfg,&f);
getextdesc(&scfg, dir, f.datoffset, extdesc);
/* Remove Ctrl-A Codes and Ex-ASCII code */
SAFEPRINTF3(vpath,"/%s/%s/%s"
,scfg.dir[dir]->code_suffix
JS_RESUMEREQUEST(js_cx, rc);
js_add_file(js_cx
,file_array
,getfname(g.gl_pathv[i]) /* filename */
,f.desc /* description */
,f.misc&FM_EXTDESC ? extdesc : NULL
,f.size /* size */
,f.cdt /* credits */
,f.date /* time */
,f.dateuled /* uploaded */
,f.datedled /* last downloaded */
,f.timesdled /* times downloaded */
,f.misc /* misc */
,getfname(g.gl_pathv[i]) /* link */
rc=JS_SUSPENDREQUEST(js_cx);
JS_RESUMEREQUEST(js_cx, rc);
JS_ClearPendingException(js_cx);
if((js_script=JS_CompileFile(js_cx, parent, spath))==NULL) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to compile script (%s)",sock,spath);
js_PrepareToExecute(js_cx, parent, spath, /* startup_dir: */NULL);
if((success=JS_ExecuteScript(js_cx, parent, js_script, &rval))!=TRUE) {
lprintf(LOG_ERR,"%04d !JavaScript FAILED to execute script (%s)",sock,spath);
lprintf(LOG_DEBUG,"%04d JavaScript: Done executing script: %s (%.2Lf seconds)"
,sock,spath,xp_timer()-start);
JS_DestroyScript(js_cx, js_script);
JS_DeleteProperty(js_cx, parent, "path");
JS_DeleteProperty(js_cx, parent, "sort");
JS_DeleteProperty(js_cx, parent, "reverse");
JS_DeleteProperty(js_cx, parent, "file_list");
JS_DeleteProperty(js_cx, parent, "dir_list");
JS_DeleteProperty(js_cx, parent, "curlib");
JS_DeleteProperty(js_cx, parent, "curdir");
JS_DeleteProperty(js_cx, parent, "html_index_file");
#endif /* ifdef JAVASCRIPT */
BOOL upload_stats(ulong bytes)
{
char str[MAX_PATH+1];

rswindell
committed
sprintf(str,"%sdsts.dab",scfg.ctrl_dir);
if((file=nopen(str,O_RDWR))==-1)
return(FALSE);
lseek(file,20L,SEEK_SET); /* Skip timestamp, logons and logons today */
read(file,&val,4); /* Uploads today */
val++;
lseek(file,-4L,SEEK_CUR);
write(file,&val,4);
read(file,&val,4); /* Upload bytes today */
val+=bytes;
lseek(file,-4L,SEEK_CUR);
write(file,&val,4);
close(file);
return(TRUE);
}
BOOL download_stats(ulong bytes)
{
char str[MAX_PATH+1];

rswindell
committed
sprintf(str,"%sdsts.dab",scfg.ctrl_dir);
if((file=nopen(str,O_RDWR))==-1)
return(FALSE);
lseek(file,28L,SEEK_SET); /* Skip timestamp, logons and logons today */
read(file,&val,4); /* Downloads today */
val++;
lseek(file,-4L,SEEK_CUR);
write(file,&val,4);
read(file,&val,4); /* Download bytes today */
val+=bytes;
lseek(file,-4L,SEEK_CUR);
write(file,&val,4);
close(file);
return(TRUE);
}
void recverror(SOCKET socket, int rd, int line)
lprintf(LOG_NOTICE,"%04d Socket closed by peer on receive (line %u)"
lprintf(LOG_NOTICE,"%04d Connection reset by peer on receive (line %u)"
lprintf(LOG_NOTICE,"%04d Connection aborted by peer on receive (line %u)"
lprintf(LOG_NOTICE,"%04d !ERROR %d receiving on socket (line %u)"
lprintf(LOG_WARNING,"%04d !ERROR: recv on socket returned unexpected value: %d (line %u)"
}
int sockreadline(SOCKET socket, char* buf, int len, time_t* lastactive)
{
char ch;
int i,rd=0;

rswindell
committed
fd_set socket_set;
struct timeval tv;
buf[0]=0;
if(socket==INVALID_SOCKET) {
lprintf(LOG_WARNING,"INVALID SOCKET in call to sockreadline");
return(0);
}

rswindell
committed
tv.tv_sec=startup->max_inactivity;
tv.tv_usec=0;

rswindell
committed
FD_ZERO(&socket_set);
FD_SET(socket,&socket_set);
i=select(socket+1,&socket_set,NULL,NULL,&tv);
if(server_socket==INVALID_SOCKET || terminate_server) {
sockprintf(socket,"421 Server downed, aborting.");
lprintf(LOG_WARNING,"%04d Server downed, aborting",socket);
return(0);
}
if(i<1) {

rswindell
committed
if(i==0) {
if((time(NULL)-(*lastactive))>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",socket);
sockprintf(socket,"421 Disconnecting due to inactivity (%u seconds)."
,startup->max_inactivity);
return(0);
}
continue;
}
#ifdef SOCKET_DEBUG_RECV_CHAR
socket_debug[socket]|=SOCKET_DEBUG_RECV_CHAR;

rswindell
committed
i=recv(socket, &ch, 1, 0);
#ifdef SOCKET_DEBUG_RECV_CHAR
socket_debug[socket]&=~SOCKET_DEBUG_RECV_CHAR;

rswindell
committed
#endif

rswindell
committed
return(i);
}
if(ch=='\n' /* && rd>=1 */) { /* Mar-9-2003: terminate on sole LF */
break;
}
buf[rd++]=ch;
}
if(rd>0 && buf[rd-1]=='\r')
buf[rd-1]=0;
else
buf[rd]=0;
#if 0 /* now exported from in xtrn.cpp */
/*****************************************************************************/
/* Returns command line generated from instr with %c replacments */
/*****************************************************************************/
static char* cmdstr(user_t* user, char *instr, char *fpath, char *fspec, char *cmd)
{
char str[256];
int i,j,len;
#ifdef _WIN32
char sfpath[MAX_PATH+1];
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
#endif
len=strlen(instr);
for(i=j=0;i<len;i++) {
if(instr[i]=='%') {
i++;
cmd[j]=0;
switch(toupper(instr[i])) {
case 'A': /* User alias */
strcat(cmd,user->alias);
break;
case 'B': /* Baud (DTE) Rate */
case 'C': /* Connect Description */
case 'D': /* Connect (DCE) Rate */
case 'E': /* Estimated Rate */
case 'H': /* Port Handle or Hardware Flow Control */
case 'P': /* COM Port */
case 'R': /* Rows */
case 'T': /* Time left in seconds */
case '&': /* Address of msr */
case 'Y': /* COMSPEC */
/* UNSUPPORTED */
break;
case 'F': /* File path */
strcat(cmd,fpath);
break;
case 'G': /* Temp directory */
strcat(cmd,scfg.temp_dir);
break;
case 'I': /* UART IRQ Line */
strcat(cmd,ultoa(scfg.com_irq,str,10));
break;
case 'J':
strcat(cmd,scfg.data_dir);
break;
case 'K':
strcat(cmd,scfg.ctrl_dir);
break;
case 'L': /* Lines per message */
strcat(cmd,ultoa(scfg.level_linespermsg[user->level],str,10));
break;
case 'M': /* Minutes (credits) for user */
strcat(cmd,ultoa(user->min,str,10));
break;
case 'N': /* Node Directory (same as SBBSNODE environment var) */
strcat(cmd,scfg.node_dir);
break;
case 'O': /* SysOp */
strcat(cmd,scfg.sys_op);
break;
case 'Q': /* QWK ID */
strcat(cmd,scfg.sys_id);
break;
case 'S': /* File Spec */
strcat(cmd,fspec);
break;
case 'U': /* UART I/O Address (in hex) */
strcat(cmd,ultoa(scfg.com_base,str,16));
break;
case 'V': /* Synchronet Version */
sprintf(str,"%s%c",VERSION,REVISION);
break;
case 'W': /* Time-slice API type (mswtype) */
break;
case 'X':
strcat(cmd,scfg.shell[user->shell]->code);
break;
case 'Z':
strcat(cmd,scfg.text_dir);
break;
case '~': /* DOS-compatible (8.3) filename */
#ifdef _WIN32
GetShortPathName(fpath,sfpath,sizeof(sfpath));
strcat(cmd,sfpath);
#else
strcat(cmd,fpath);
#endif
break;
case '!': /* EXEC Directory */
strcat(cmd,scfg.exec_dir);
break;
case '@': /* EXEC Directory for DOS/OS2/Win32, blank for Unix */
#ifndef __unix__
strcat(cmd,scfg.exec_dir);
#endif
break;
case '#': /* Node number (same as SBBSNNUM environment var) */
sprintf(str,"%u",scfg.node_num);
strcat(cmd,str);
break;
case '*':
sprintf(str,"%03u",scfg.node_num);
strcat(cmd,str);
break;
case '$': /* Credits */
strcat(cmd,ultoa(user->cdt+user->freecdt,str,10));
break;
case '%': /* %% for percent sign */
strcat(cmd,"%");
break;
case '?': /* Platform */
#ifdef __OS2__
SAFECOPY(str,PLATFORM_DESC);
#endif
strlwr(str);
strcat(cmd,str);
break;
default: /* unknown specification */
if(isdigit(instr[i])) {
sprintf(str,"%0*d",instr[i]&0xf,user->number);
strcat(cmd,str); }
break; }
j=strlen(cmd); }
else
cmd[j++]=instr[i]; }
cmd[j]=0;
return(cmd);
}
#endif
void DLLCALL ftp_terminate(void)
lprintf(LOG_INFO,"%04d FTP Server terminate",server_socket);
terminate_server=TRUE;
}
typedef struct {
SOCKET ctrl_sock;
SOCKET* data_sock;
BOOL* inprogress;
BOOL* aborted;
BOOL delfile;
BOOL tmpfile;
BOOL credits;
BOOL append;
long filepos;
char filename[MAX_PATH+1];
time_t* lastactive;
user_t* user;
client_t* client;
int dir;
char* desc;
} xfer_t;
static void send_thread(void* arg)
{
char buf[8192];
char fname[MAX_PATH+1];
char str[128];
char username[128];
long mod;
ulong l;
ulong last_total=0;
ulong dur;
ulong cps;
ulong length;
BOOL error=FALSE;
FILE* fp;
file_t f;
xfer_t xfer;
time_t now;
time_t start;
time_t last_report;
SOCKADDR_IN addr;
socklen_t addr_len;
fd_set socket_set;
struct timeval tv;
free(arg);
SetThreadName("FTP Send");
thread_up(TRUE /* setuid */);
length=flength(xfer.filename);
if((fp=fnopen(NULL,xfer.filename,O_RDONLY|O_BINARY))==NULL /* non-shareable open failed */
&& (fp=fopen(xfer.filename,"rb"))==NULL) { /* shareable open failed */
lprintf(LOG_ERR,"%04d !DATA ERROR %d opening %s",xfer.ctrl_sock,errno,xfer.filename);
sockprintf(xfer.ctrl_sock,"450 ERROR %d opening %s.",errno,xfer.filename);
if(xfer.tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES))

rswindell
committed
ftp_close_socket(xfer.data_sock,__LINE__);
thread_down();
#ifdef SOCKET_DEBUG_SENDTHREAD
socket_debug[xfer.ctrl_sock]|=SOCKET_DEBUG_SENDTHREAD;
#endif
if(startup->options&FTP_OPT_DEBUG_DATA || xfer.filepos)
lprintf(LOG_DEBUG,"%04d DATA socket %d sending %s from offset %lu"
,xfer.ctrl_sock,*xfer.data_sock,xfer.filename,xfer.filepos);
fseek(fp,xfer.filepos,SEEK_SET);
last_report=start=time(NULL);
while((xfer.filepos+total)<length) {
/* Periodic progress report */
if(total && now>=last_report+XFER_REPORT_INTERVAL) {
if(xfer.filepos)
sprintf(str," from offset %lu",xfer.filepos);
else
str[0]=0;
lprintf(LOG_INFO,"%04d Sent %lu bytes (%lu total) of %s (%lu cps)%s"
,xfer.ctrl_sock,total,length,xfer.filename
,(ulong)((total-last_total)/(now-last_report))
,str);
last_total=total;
last_report=now;
}
lprintf(LOG_WARNING,"%04d !DATA Transfer aborted",xfer.ctrl_sock);
sockprintf(xfer.ctrl_sock,"426 Transfer aborted.");
error=TRUE;
break;
}
if(server_socket==INVALID_SOCKET || terminate_server) {
lprintf(LOG_WARNING,"%04d !DATA Transfer locally aborted",xfer.ctrl_sock);
sockprintf(xfer.ctrl_sock,"426 Transfer locally aborted.");
error=TRUE;
break;
}
/* Check socket for writability (using select) */
tv.tv_sec=1;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(*xfer.data_sock,&socket_set);
i=select((*xfer.data_sock)+1,NULL,&socket_set,NULL,&tv);
if(i==SOCKET_ERROR) {
lprintf(LOG_WARNING,"%04d !DATA ERROR %d selecting socket %d for send"
,xfer.ctrl_sock, ERROR_VALUE, *xfer.data_sock);
sockprintf(xfer.ctrl_sock,"426 Transfer error.");
error=TRUE;
break;
}
fseek(fp,xfer.filepos+total,SEEK_SET);
rd=fread(buf,sizeof(char),sizeof(buf),fp);
socket_debug[xfer.ctrl_sock]|=SOCKET_DEBUG_SEND;
#endif
wr=sendsocket(*xfer.data_sock,buf,rd);
socket_debug[xfer.ctrl_sock]&=~SOCKET_DEBUG_SEND;
#endif
if(ERROR_VALUE==EWOULDBLOCK) {
/*lprintf(LOG_WARNING,"%04d DATA send would block, retrying",xfer.ctrl_sock);*/
YIELD();
continue;
}
else if(ERROR_VALUE==ECONNRESET)
lprintf(LOG_WARNING,"%04d DATA Connection reset by peer, sending on socket %d"
,xfer.ctrl_sock,*xfer.data_sock);
lprintf(LOG_WARNING,"%04d DATA Connection aborted by peer, sending on socket %d"
lprintf(LOG_WARNING,"%04d !DATA ERROR %d sending on data socket %d"
,xfer.ctrl_sock,ERROR_VALUE,*xfer.data_sock);
/* Send NAK */
sockprintf(xfer.ctrl_sock,"426 Error %d sending on DATA channel"
,ERROR_VALUE);
error=TRUE;
break;
}
if(wr==0) {
lprintf(LOG_WARNING,"%04d !DATA socket %d disconnected",xfer.ctrl_sock, *xfer.data_sock);
sockprintf(xfer.ctrl_sock,"426 DATA channel disconnected");
error=TRUE;
break;
}
lprintf(LOG_ERR,"%04d !DATA SEND ERROR %d (%d) on socket %d"
,xfer.ctrl_sock, wr, ERROR_VALUE, *xfer.data_sock);
sockprintf(xfer.ctrl_sock,"451 DATA send error");
error=TRUE;
break;
}
total+=wr;
*xfer.lastactive=time(NULL);
YIELD();
if((i=ferror(fp))!=0)
lprintf(LOG_ERR,"%04d !FILE ERROR %d (%d)",xfer.ctrl_sock,i,errno);

rswindell
committed
ftp_close_socket(xfer.data_sock,__LINE__); /* Signal end of file */
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf(LOG_DEBUG,"%04d DATA socket closed",xfer.ctrl_sock);
if(!error) {
dur=time(NULL)-start;
cps=dur ? total/dur : total*2;
lprintf(LOG_INFO,"%04d Transfer successful: %lu bytes sent in %lu seconds (%lu cps)"
,xfer.ctrl_sock
,total,dur,cps);
sockprintf(xfer.ctrl_sock,"226 Download complete (%lu cps).",cps);
if(xfer.dir>=0) {
memset(&f,0,sizeof(f));
GetShortPathName(xfer.filename,fname,sizeof(fname));
SAFECOPY(fname,xfer.filename);
#endif
padfname(getfname(fname),f.name);
f.dir=xfer.dir;
f.size=total;
if(getfileixb(&scfg,&f)==TRUE && getfiledat(&scfg,&f)==TRUE) {
f.timesdled++;
putfiledat(&scfg,&f);
f.datedled=time(NULL);
putfileixb(&scfg,&f);
lprintf(LOG_INFO,"%04d %s downloaded: %s (%lu times total)"
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
,xfer.ctrl_sock
,xfer.user->alias
,xfer.filename
,f.timesdled);
/**************************/
/* Update Uploader's Info */
/**************************/
uploader.number=matchuser(&scfg,f.uler,TRUE /*sysop_alias*/);
if(uploader.number
&& uploader.number!=xfer.user->number
&& getuserdat(&scfg,&uploader)==0
&& uploader.firston<f.dateuled) {
l=f.cdt;
if(!(scfg.dir[f.dir]->misc&DIR_CDTDL)) /* Don't give credits on d/l */
l=0;
if(scfg.dir[f.dir]->misc&DIR_CDTMIN && cps) { /* Give min instead of cdt */
mod=((ulong)(l*(scfg.dir[f.dir]->dn_pct/100.0))/cps)/60;
adjustuserrec(&scfg,uploader.number,U_MIN,10,mod);
sprintf(tmp,"%lu minute",mod);
} else {
mod=(ulong)(l*(scfg.dir[f.dir]->dn_pct/100.0));
adjustuserrec(&scfg,uploader.number,U_CDT,10,mod);
ultoac(mod,tmp);
}
if(!(scfg.dir[f.dir]->misc&DIR_QUIET)) {
addr_len = sizeof(addr);
if(uploader.level>=SYSOP_LEVEL
&& getpeername(xfer.ctrl_sock,(struct sockaddr *)&addr,&addr_len)==0)
SAFEPRINTF2(username,"%s [%s]",xfer.user->alias,inet_ntoa(addr.sin_addr));
else
SAFECOPY(username,xfer.user->alias);
/* Inform uploader of downloaded file */
safe_snprintf(str,sizeof(str),text[DownloadUserMsg]
,getfname(xfer.filename)
,xfer.filepos ? "partially FTP-" : "FTP-"
,username,tmp);
putsmsg(&scfg,uploader.number,str);
}
if(!xfer.tmpfile && !xfer.delfile && !(scfg.dir[f.dir]->misc&DIR_NOSTAT))
download_stats(total);
}
if(xfer.credits) {
user_downloaded(&scfg, xfer.user, 1, total);
if(xfer.dir>=0 && !is_download_free(&scfg,xfer.dir,xfer.user,xfer.client))
subtract_cdt(&scfg, xfer.user, xfer.credits);
}
}
fclose(fp);
if(server_socket!=INVALID_SOCKET && !terminate_server)
if(xfer.tmpfile) {
if(!(startup->options&FTP_OPT_KEEP_TEMP_FILES))
remove(xfer.filename);
}
else if(xfer.delfile && !error)
#if defined(SOCKET_DEBUG_SENDTHREAD)
socket_debug[xfer.ctrl_sock]&=~SOCKET_DEBUG_SENDTHREAD;
#endif
thread_down();
}
static void receive_thread(void* arg)
{
char* p;
char str[128];
char ext[F_EXBSIZE+1];
char desc[F_EXBSIZE+1];
char cmd[MAX_PATH*2];
char tmp[MAX_PATH+1];
char fname[MAX_PATH+1];
int rd;
int file;
ulong total=0;
ulong last_total=0;
ulong dur;
ulong cps;
BOOL error=FALSE;
FILE* fp;
file_t f;
xfer_t xfer;
time_t now;
time_t start;
time_t last_report;
fd_set socket_set;
struct timeval tv;
free(arg);
SetThreadName("FTP RECV");
thread_up(TRUE /* setuid */);
if((fp=fopen(xfer.filename,xfer.append ? "ab" : "wb"))==NULL) {
lprintf(LOG_ERR,"%04d !DATA ERROR %d opening %s",xfer.ctrl_sock,errno,xfer.filename);
sockprintf(xfer.ctrl_sock,"450 ERROR %d opening %s.",errno,xfer.filename);

rswindell
committed
ftp_close_socket(xfer.data_sock,__LINE__);
thread_down();
if(xfer.append)
xfer.filepos=filelength(fileno(fp));

rswindell
committed
if(xfer.filepos || startup->options&FTP_OPT_DEBUG_DATA)
lprintf(LOG_DEBUG,"%04d DATA socket %d receiving %s from offset %lu"
,xfer.ctrl_sock,*xfer.data_sock,xfer.filename,xfer.filepos);
fseek(fp,xfer.filepos,SEEK_SET);
last_report=start=time(NULL);
while(1) {
now=time(NULL);
/* Periodic progress report */
if(total && now>=last_report+XFER_REPORT_INTERVAL) {
if(xfer.filepos)
sprintf(str," from offset %lu",xfer.filepos);
else
str[0]=0;
lprintf(LOG_INFO,"%04d Received %lu bytes of %s (%lu cps)%s"
,xfer.ctrl_sock,total,xfer.filename
,(total-last_total)/(now-last_report)
,str);
last_total=total;
last_report=now;
}
if(*xfer.aborted==TRUE) {
lprintf(LOG_WARNING,"%04d !DATA Transfer aborted",xfer.ctrl_sock);

rswindell
committed
/* Send NAK */
sockprintf(xfer.ctrl_sock,"426 Transfer aborted.");
error=TRUE;
break;
}
if(server_socket==INVALID_SOCKET || terminate_server) {
lprintf(LOG_WARNING,"%04d !DATA Transfer locally aborted",xfer.ctrl_sock);

rswindell
committed
/* Send NAK */
sockprintf(xfer.ctrl_sock,"426 Transfer locally aborted.");
error=TRUE;
break;
}
/* Check socket for readability (using select) */
tv.tv_sec=1;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(*xfer.data_sock,&socket_set);
i=select((*xfer.data_sock)+1,&socket_set,NULL,NULL,&tv);
if(i==SOCKET_ERROR) {
lprintf(LOG_WARNING,"%04d !DATA ERROR %d selecting socket %d for receive"
,xfer.ctrl_sock, ERROR_VALUE, *xfer.data_sock);
sockprintf(xfer.ctrl_sock,"426 Transfer error.");
error=TRUE;
break;
}
#if defined(SOCKET_DEBUG_RECV_BUF)
socket_debug[xfer.ctrl_sock]|=SOCKET_DEBUG_RECV_BUF;
rd=recv(*xfer.data_sock,buf,sizeof(buf),0);
#if defined(SOCKET_DEBUG_RECV_BUF)
socket_debug[xfer.ctrl_sock]&=~SOCKET_DEBUG_RECV_BUF;
if(rd<1) {
if(rd==0) { /* Socket closed */
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf(LOG_DEBUG,"%04d DATA socket %d closed by client"
,xfer.ctrl_sock,*xfer.data_sock);
break;
}
if(rd==SOCKET_ERROR) {
if(ERROR_VALUE==EWOULDBLOCK) {
/*lprintf(LOG_WARNING,"%04d DATA recv would block, retrying",xfer.ctrl_sock);*/
YIELD();
continue;
}
else if(ERROR_VALUE==ECONNRESET)
lprintf(LOG_WARNING,"%04d DATA Connection reset by peer, receiving on socket %d"
,xfer.ctrl_sock,*xfer.data_sock);
lprintf(LOG_WARNING,"%04d DATA Connection aborted by peer, receiving on socket %d"
lprintf(LOG_WARNING,"%04d !DATA ERROR %d receiving on data socket %d"
,xfer.ctrl_sock,ERROR_VALUE,*xfer.data_sock);

rswindell
committed
/* Send NAK */
sockprintf(xfer.ctrl_sock,"426 Error %d receiving on DATA channel"
,ERROR_VALUE);
error=TRUE;
break;
}
lprintf(LOG_ERR,"%04d !DATA ERROR recv returned %d on socket %d"
,xfer.ctrl_sock,rd,*xfer.data_sock);

rswindell
committed
/* Send NAK */
sockprintf(xfer.ctrl_sock,"451 Unexpected socket error: %d",rd);
error=TRUE;
break;
}
fwrite(buf,1,rd,fp);
total+=rd;
*xfer.lastactive=time(NULL);
YIELD();

rswindell
committed
fclose(fp);

rswindell
committed
ftp_close_socket(xfer.data_sock,__LINE__);
if(error && startup->options&FTP_OPT_DEBUG_DATA)
lprintf(LOG_DEBUG,"%04d DATA socket %d closed",xfer.ctrl_sock,*xfer.data_sock);
if(!error) {
dur=time(NULL)-start;
cps=dur ? total/dur : total*2;
lprintf(LOG_INFO,"%04d Transfer successful: %lu bytes received in %lu seconds (%lu cps)"
,xfer.ctrl_sock
,total,dur,cps);
if(xfer.dir>=0) {
memset(&f,0,sizeof(f));
GetShortPathName(xfer.filename,fname,sizeof(fname));
SAFECOPY(fname,xfer.filename);
#endif
padfname(getfname(fname),f.name);
filedat=getfileixb(&scfg,&f);
if(scfg.dir[f.dir]->misc&DIR_AONLY) /* Forced anonymous */
f.misc|=FM_ANON;
f.cdt=flength(xfer.filename);
/* Desciption specified with DESC command? */
if(xfer.desc!=NULL && *xfer.desc!=0)
/* Necessary for DIR and LIB ARS keyword support in subsequent chk_ar()'s */
SAFECOPY(xfer.user->curdir, scfg.dir[f.dir]->code);
p=strrchr(f.name,'.');
if(p!=NULL && scfg.dir[f.dir]->misc&DIR_DIZ) {
for(i=0;i<scfg.total_fextrs;i++)
if(!stricmp(scfg.fextr[i]->ext,p+1)
&& chk_ar(&scfg,scfg.fextr[i]->ar,xfer.user,xfer.client))
break;
if(i<scfg.total_fextrs) {
sprintf(tmp,"%sFILE_ID.DIZ",scfg.temp_dir);
if(fexistcase(tmp))
remove(tmp);
cmdstr(&scfg,xfer.user,scfg.fextr[i]->cmd,fname,"FILE_ID.DIZ",cmd);
lprintf(LOG_DEBUG,"%04d Extracting DIZ: %s",xfer.ctrl_sock,cmd);
system(cmd);
if(!fexistcase(tmp)) {
sprintf(tmp,"%sDESC.SDI",scfg.temp_dir);
if(fexistcase(tmp))
remove(tmp);
cmdstr(&scfg,xfer.user,scfg.fextr[i]->cmd,fname,"DESC.SDI",cmd);
lprintf(LOG_DEBUG,"%04d Extracting DIZ: %s",xfer.ctrl_sock,cmd);
system(cmd);
fexistcase(tmp); /* fixes filename case */
}
if((file=nopen(tmp,O_RDONLY))!=-1) {
lprintf(LOG_DEBUG,"%04d Parsing DIZ: %s",xfer.ctrl_sock,tmp);
memset(ext,0,sizeof(ext));
read(file,ext,sizeof(ext)-1);
for(i=sizeof(ext)-1;i;i--) /* trim trailing spaces */
if(!f.desc[0]) { /* use for normal description */
strip_exascii(desc, desc); /* strip extended ASCII chars */
prep_file_desc(desc, desc); /* strip control chars and dupe chars */
for(i=0;desc[i];i++) /* find approprate first char */
if(isalnum(desc[i]))
break;
}
close(file);
remove(tmp);
f.misc|=FM_EXTDESC;
} else
lprintf(LOG_DEBUG,"%04d DIZ Does not exist: %s",xfer.ctrl_sock,tmp);
}
} /* FILE_ID.DIZ support */
if(f.desc[0]==0) /* no description given, use (long) filename */
SAFECOPY(f.uler,xfer.user->alias); /* exception here, Aug-27-2002 */
if(filedat) {
if(!putfiledat(&scfg,&f))
lprintf(LOG_ERR,"%04d !ERROR updating file (%s) in database",xfer.ctrl_sock,f.name);
/* need to update the index here */
} else {
if(!addfiledat(&scfg,&f))
lprintf(LOG_ERR,"%04d !ERROR adding file (%s) to database",xfer.ctrl_sock,f.name);
if(f.misc&FM_EXTDESC)
putextdesc(&scfg,f.dir,f.datoffset,ext);
if(scfg.dir[f.dir]->upload_sem[0])
ftouch(scfg.dir[f.dir]->upload_sem);
/**************************/
/* Update Uploader's Info */
/**************************/
user_uploaded(&scfg, xfer.user, (!xfer.append && xfer.filepos==0) ? 1:0, total);
if(scfg.dir[f.dir]->up_pct && scfg.dir[f.dir]->misc&DIR_CDTUL) { /* credit for upload */
if(scfg.dir[f.dir]->misc&DIR_CDTMIN && cps) /* Give min instead of cdt */
xfer.user->min=adjustuserrec(&scfg,xfer.user->number,U_MIN,10
,((ulong)(total*(scfg.dir[f.dir]->up_pct/100.0))/cps)/60);
else
xfer.user->cdt=adjustuserrec(&scfg,xfer.user->number,U_CDT,10
,(ulong)(f.cdt*(scfg.dir[f.dir]->up_pct/100.0)));
}
if(!(scfg.dir[f.dir]->misc&DIR_NOSTAT))
upload_stats(total);

rswindell
committed
/* Send ACK */
sockprintf(xfer.ctrl_sock,"226 Upload complete (%lu cps).",cps);
if(server_socket!=INVALID_SOCKET && !terminate_server)
*xfer.inprogress=FALSE;
thread_down();
}
static void filexfer(SOCKADDR_IN* addr, SOCKET ctrl_sock, SOCKET pasv_sock, SOCKET* data_sock
,char* filename, long filepos, BOOL* inprogress, BOOL* aborted
,BOOL delfile, BOOL tmpfile
,time_t* lastactive
,user_t* user
,int dir
,BOOL receiving
,BOOL credits
,BOOL append
,char* desc)
{
int result;
ulong l;
Loading
Loading full blame...