Skip to content
Snippets Groups Projects
ftpsrvr.c 177 KiB
Newer Older
					,scfg.dir[i]->misc			/* misc */
					,scfg.sys_id				/* uploader */
rswindell's avatar
rswindell committed
		} else if(chk_ar(&scfg,scfg.dir[dir]->ar,user,client)){
			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
				SAFECOPY(str,g.gl_pathv[i]);
	#endif
				padfname(getfname(str),f.name);
				f.dir=dir;
				if(getfileixb(&scfg,&f)) {
					f.size=0; /* flength(g.gl_pathv[i]); */
					getfiledat(&scfg,&f);
					if(f.misc&FM_EXTDESC) {
						extdesc[0]=0;
						getextdesc(&scfg, dir, f.datoffset, extdesc);
						/* Remove Ctrl-A Codes and Ex-ASCII code */
						remove_ctrl_a(extdesc,extdesc);
						,scfg.lib[scfg.dir[dir]->lib]->sname
						,getfname(g.gl_pathv[i]));
					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.uler						/* uploader */
						,getfname(g.gl_pathv[i])	/* link */
					rc=JS_SUSPENDREQUEST(js_cx);
			JS_RESUMEREQUEST(js_cx, rc);
		/* RUN SCRIPT */
		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, parent);
		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_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)
{
deuce's avatar
deuce committed
	uint32_t	val;
	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)
{
deuce's avatar
deuce committed
	uint32_t	val;
	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)"
			,socket, line);
	else if(rd==SOCKET_ERROR) {
rswindell's avatar
rswindell committed
		if(ERROR_VALUE==ECONNRESET) 
			lprintf(LOG_NOTICE,"%04d Connection reset by peer on receive (line %u)"
				,socket, line);
		else if(ERROR_VALUE==ECONNABORTED) 
			lprintf(LOG_NOTICE,"%04d Connection aborted by peer on receive (line %u)"
				,socket, line);
			lprintf(LOG_NOTICE,"%04d !ERROR %d receiving on socket (line %u)"
				,socket, ERROR_VALUE, line);
		lprintf(LOG_WARNING,"%04d !ERROR: recv on socket returned unexpected value: %d (line %u)"
			,socket, rd, line);
deuce's avatar
deuce committed
static int sock_recvbyte(SOCKET sock, CRYPT_SESSION sess, char *buf, time_t *lastactive)
deuce's avatar
deuce committed
	int len=0;
deuce's avatar
deuce committed
	struct	timeval	tv;
	int ret;
	int i;
deuce's avatar
deuce committed
	if(ftp_set==NULL || terminate_server) {
		sockprintf(sock,sess,"421 Server downed, aborting.");
		lprintf(LOG_WARNING,"%04d Server downed, aborting",sock);
deuce's avatar
deuce committed
	if (sess > -1) {
		/* Try a read with no timeout first. */
		cryptSetAttribute(sess, CRYPT_OPTION_NET_READTIMEOUT, 0);
		while (1) {
			ret = cryptPopData(sess, buf, 1, &len);
			/* Successive reads will be with the full timeout after a select() */
			cryptSetAttribute(sess, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity);
			switch(ret) {
				case CRYPT_OK:
					break;
				case CRYPT_ERROR_TIMEOUT:
					lprintf(LOG_WARNING,"%04d !TIMEOUT in sock_recvbyte (%u seconds):  INACTIVE SOCKET",sock,startup->max_inactivity);
					return -1;
				case CRYPT_ERROR_COMPLETE:
					return 0;
				default:
					lprintf(LOG_WARNING,"%04d !Cryptlib error in sock_recvbyte:  %d",sock,ret);
					if (ret < -1)
						return ret;
					return -2;
			}
			if (len)
				return len;
			
			if((time(NULL)-(*lastactive))>startup->max_inactivity) {
				lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",sock);
				sockprintf(sock,sess,"421 Disconnecting due to inactivity (%u seconds)."
					,startup->max_inactivity);
				return(0);
			}
deuce's avatar
deuce committed
			tv.tv_sec=startup->max_inactivity;
			tv.tv_usec=0;
deuce's avatar
deuce committed
			FD_ZERO(&socket_set);
			FD_SET(sock,&socket_set);
deuce's avatar
deuce committed
			i=select(sock+1,&socket_set,NULL,NULL,&tv);
deuce's avatar
deuce committed
			if(i<1) {
				if(i==0) {
					if((time(NULL)-(*lastactive))>startup->max_inactivity) {
						lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",sock);
						sockprintf(sock,sess,"421 Disconnecting due to inactivity (%u seconds)."
							,startup->max_inactivity);
						return(0);
					}
					continue;
				}
				recverror(sock,i,__LINE__);
				return(i);
			}
deuce's avatar
deuce committed
	}
	else {
		while (1) {
			tv.tv_sec=startup->max_inactivity;
			tv.tv_usec=0;

			FD_ZERO(&socket_set);
			FD_SET(sock,&socket_set);

			i=select(sock+1,&socket_set,NULL,NULL,&tv);

			if(i<1) {
				if(i==0) {
					if((time(NULL)-(*lastactive))>startup->max_inactivity) {
						lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",sock);
						sockprintf(sock,sess,"421 Disconnecting due to inactivity (%u seconds)."
							,startup->max_inactivity);
						return(0);
					}
					continue;
deuce's avatar
deuce committed
				recverror(sock,i,__LINE__);
				return(i);
deuce's avatar
deuce committed
	#ifdef SOCKET_DEBUG_RECV_CHAR
			socket_debug[sock]|=SOCKET_DEBUG_RECV_CHAR;
	#endif
			i=recv(sock, buf, 1, 0);
	#ifdef SOCKET_DEBUG_RECV_CHAR
			socket_debug[sock]&=~SOCKET_DEBUG_RECV_CHAR;
	#endif
			return i;
deuce's avatar
deuce committed
	}
}

int sockreadline(SOCKET socket, CRYPT_SESSION sess, char* buf, int len, time_t* lastactive)
{
	char	ch;
	int		i,rd=0;

	buf[0]=0;

	if(socket==INVALID_SOCKET) {
		lprintf(LOG_WARNING,"INVALID SOCKET in call to sockreadline");
		return(0);
	}

	while(rd<len-1) {
		i = sock_recvbyte(socket, sess, &ch, lastactive);

			recverror(socket,i,__LINE__);
		if(ch=='\n' /* && rd>=1 */) { /* Mar-9-2003: terminate on sole LF */
	if(rd>0 && buf[rd-1]=='\r')
		buf[rd-1]=0;
	else
		buf[rd]=0;
deuce's avatar
deuce committed

deuce's avatar
deuce committed
   	lprintf(LOG_INFO,"FTP Server terminate");
int ftp_remove(SOCKET sock, int line, const char* fname)
{
	int ret=0;

	if(fexist(fname) && (ret=remove(fname))!=0) {
		if(fexist(fname))	// In case there was a race condition (other host deleted file first)
			lprintf(LOG_ERR,"%04d !ERROR %d (%s) (line %d) removing file: %s", sock, errno, STRERROR(errno), line, fname);
	}
rswindell's avatar
rswindell committed
	SOCKET		ctrl_sock;
deuce's avatar
deuce committed
	CRYPT_SESSION	ctrl_sess;
rswindell's avatar
rswindell committed
	SOCKET*		data_sock;
deuce's avatar
deuce committed
	CRYPT_SESSION*	data_sess;
rswindell's avatar
rswindell committed
	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];
deuce's avatar
deuce committed
	char		host_ip[INET6_ADDRSTRLEN];
	ulong		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;
deuce's avatar
deuce committed
	union xp_sockaddr	addr;
	fd_set		socket_set;
	struct timeval tv;

	xfer=*(xfer_t*)arg;
	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);
deuce's avatar
deuce committed
		sockprintf(xfer.ctrl_sock,xfer.ctrl_sess,"450 ERROR %d opening %s.",errno,xfer.filename);
		if(xfer.tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES))
			ftp_remove(xfer.ctrl_sock, __LINE__, xfer.filename);
deuce's avatar
deuce committed
		ftp_close_socket(xfer.data_sock,xfer.data_sess,__LINE__);
		*xfer.inprogress=FALSE;
#ifdef SOCKET_DEBUG_SENDTHREAD
rswindell's avatar
rswindell committed
			socket_debug[xfer.ctrl_sock]|=SOCKET_DEBUG_SENDTHREAD;
#endif

	*xfer.aborted=FALSE;
	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);
		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 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;
		}

		if(*xfer.aborted==TRUE) {
			lprintf(LOG_WARNING,"%04d !DATA Transfer aborted",xfer.ctrl_sock);
deuce's avatar
deuce committed
			sockprintf(xfer.ctrl_sock,xfer.ctrl_sess,"426 Transfer aborted.");
deuce's avatar
deuce committed
		if(ftp_set==NULL || terminate_server) {
			lprintf(LOG_WARNING,"%04d !DATA Transfer locally aborted",xfer.ctrl_sock);
deuce's avatar
deuce committed
			sockprintf(xfer.ctrl_sock,xfer.ctrl_sess,"426 Transfer locally aborted.");

		/* Check socket for writability (using select) */
		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);
deuce's avatar
deuce committed
			sockprintf(xfer.ctrl_sock,xfer.ctrl_sess,"426 Transfer error.");
deuce's avatar
deuce committed
		if(i<1)
		fseek(fp,xfer.filepos+total,SEEK_SET);
		rd=fread(buf,sizeof(char),sizeof(buf),fp);
		if(rd<1) /* EOF or READ error */
#ifdef SOCKET_DEBUG_SEND
		socket_debug[xfer.ctrl_sock]|=SOCKET_DEBUG_SEND;
#endif
deuce's avatar
deuce committed
		if (*xfer.data_sess != -1) {
			int status = cryptPushData(*xfer.data_sess, buf, rd, &wr);
			if (status != CRYPT_OK) {
				lprintf(LOG_DEBUG, "PushData() returned %d\n", status);
				wr = -1;
			}
			else {
				status = cryptFlushData(*xfer.data_sess);
				if (status != CRYPT_OK) {
					lprintf(LOG_DEBUG, "cryptFlushData() returned %d\n", status);
					wr = -1;
				}
			}
		}
		else
			wr=sendsocket(*xfer.data_sock,buf,rd);
#ifdef SOCKET_DEBUG_SEND
		socket_debug[xfer.ctrl_sock]&=~SOCKET_DEBUG_SEND;
#endif
			if(wr==SOCKET_ERROR) {
					/*lprintf(LOG_WARNING,"%04d DATA send would block, retrying",xfer.ctrl_sock);*/
					lprintf(LOG_WARNING,"%04d DATA Connection reset by peer, sending on socket %d"
						,xfer.ctrl_sock,*xfer.data_sock);
				else if(ERROR_VALUE==ECONNABORTED) 
					lprintf(LOG_WARNING,"%04d DATA Connection aborted by peer, sending on socket %d"
						,xfer.ctrl_sock,*xfer.data_sock);
					lprintf(LOG_WARNING,"%04d !DATA ERROR %d sending on data socket %d"
						,xfer.ctrl_sock,ERROR_VALUE,*xfer.data_sock);
deuce's avatar
deuce committed
				sockprintf(xfer.ctrl_sock,xfer.ctrl_sess,"426 Error %d sending on DATA channel"
				error=TRUE;
				break;
			}
			if(wr==0) {
				lprintf(LOG_WARNING,"%04d !DATA socket %d disconnected",xfer.ctrl_sock, *xfer.data_sock);
deuce's avatar
deuce committed
				sockprintf(xfer.ctrl_sock,xfer.ctrl_sess,"426 DATA channel disconnected");
			lprintf(LOG_ERR,"%04d !DATA SEND ERROR %d (%d) on socket %d"
				,xfer.ctrl_sock, wr, ERROR_VALUE, *xfer.data_sock);
deuce's avatar
deuce committed
			sockprintf(xfer.ctrl_sock,xfer.ctrl_sess,"451 DATA send error");
			error=TRUE;
			break;
		}
		total+=wr;
	if((i=ferror(fp))!=0) 
		lprintf(LOG_ERR,"%04d !FILE ERROR %d (%d)",xfer.ctrl_sock,i,errno);
deuce's avatar
deuce committed
	ftp_close_socket(xfer.data_sock,xfer.data_sess,__LINE__);	/* Signal end of file */
	if(startup->options&FTP_OPT_DEBUG_DATA)
		lprintf(LOG_DEBUG,"%04d DATA socket closed",xfer.ctrl_sock);
		dur=(long)(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);
deuce's avatar
deuce committed
		sockprintf(xfer.ctrl_sock,xfer.ctrl_sess,"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);
				lprintf(LOG_INFO,"%04d %s downloaded: %s (%lu times total)"
					,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
deuce's avatar
deuce committed
							&& getpeername(xfer.ctrl_sock,&addr.addr,&addr_len)==0
							&& inet_addrtop(&addr, host_ip, sizeof(host_ip))!=NULL)
							SAFEPRINTF2(username,"%s [%s]",xfer.user->alias,host_ip);
						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-"
			if(!xfer.tmpfile && !xfer.delfile && !(scfg.dir[f.dir]->misc&DIR_NOSTAT))
				download_stats(total);
			user_downloaded(&scfg, xfer.user, 1, total);
rswindell's avatar
rswindell committed
			if(xfer.dir>=0 && !is_download_free(&scfg,xfer.dir,xfer.user,xfer.client))
				subtract_cdt(&scfg, xfer.user, xfer.credits);
		}
	}

	fclose(fp);
deuce's avatar
deuce committed
	if(ftp_set!=NULL && !terminate_server)
		*xfer.inprogress=FALSE;
	if(xfer.tmpfile) {
		if(!(startup->options&FTP_OPT_KEEP_TEMP_FILES))
			ftp_remove(xfer.ctrl_sock, __LINE__, xfer.filename);
	} 
	else if(xfer.delfile && !error)
		ftp_remove(xfer.ctrl_sock, __LINE__, xfer.filename);
#if defined(SOCKET_DEBUG_SENDTHREAD)
rswindell's avatar
rswindell committed
			socket_debug[xfer.ctrl_sock]&=~SOCKET_DEBUG_SENDTHREAD;
#endif

	thread_down();
}

static void receive_thread(void* arg)
{
	char		buf[8192];
	char		ext[F_EXBSIZE+1];
	char		desc[F_EXBSIZE+1];
	char		cmd[MAX_PATH*2];
	char		tmp[MAX_PATH+1];
	int			rd;
	int			file;
	ulong		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;
deuce's avatar
deuce committed
	CRYPT_SESSION	sess = -1;

	xfer=*(xfer_t*)arg;
	SetThreadName("sbbs/ftpReceive");
	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);
deuce's avatar
deuce committed
		sockprintf(xfer.ctrl_sock,sess,"450 ERROR %d opening %s.",errno,xfer.filename);
		ftp_close_socket(xfer.data_sock,xfer.data_sess,__LINE__);
rswindell's avatar
rswindell committed
		*xfer.inprogress=FALSE;
	if(xfer.append)
		xfer.filepos=filelength(fileno(fp));

	*xfer.aborted=FALSE;
	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);
		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
				,(ulong)((total-last_total)/(now-last_report))
		if(startup->max_fsize && (xfer.filepos+total) > startup->max_fsize) {
			lprintf(LOG_WARNING,"%04d !DATA received %lu bytes of %s exceeds maximum allowed (%lu bytes)"
				,xfer.ctrl_sock, xfer.filepos+total, xfer.filename, startup->max_fsize);
deuce's avatar
deuce committed
			sockprintf(xfer.ctrl_sock,sess,"552 File size exceeds maximum allowed (%lu bytes)", startup->max_fsize);
		if(*xfer.aborted==TRUE) {
			lprintf(LOG_WARNING,"%04d !DATA Transfer aborted",xfer.ctrl_sock);
deuce's avatar
deuce committed
			sockprintf(xfer.ctrl_sock,sess,"426 Transfer aborted.");
deuce's avatar
deuce committed
		if(ftp_set==NULL || terminate_server) {
			lprintf(LOG_WARNING,"%04d !DATA Transfer locally aborted",xfer.ctrl_sock);
deuce's avatar
deuce committed
			sockprintf(xfer.ctrl_sock,sess,"426 Transfer locally aborted.");

		/* Check socket for readability (using select) */
		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);
deuce's avatar
deuce committed
			sockprintf(xfer.ctrl_sock,sess,"426 Transfer error.");
deuce's avatar
deuce committed
		if(i<1)
#if defined(SOCKET_DEBUG_RECV_BUF)
		socket_debug[xfer.ctrl_sock]|=SOCKET_DEBUG_RECV_BUF;
deuce's avatar
deuce committed
		if (*xfer.data_sess != -1) {
			int status = cryptPopData(*xfer.data_sess, buf, sizeof(buf), &rd);
			if (status != CRYPT_OK)
				rd = -1;
		}
		else {
			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) {
					/*lprintf(LOG_WARNING,"%04d DATA recv would block, retrying",xfer.ctrl_sock);*/
					lprintf(LOG_WARNING,"%04d DATA Connection reset by peer, receiving on socket %d"
						,xfer.ctrl_sock,*xfer.data_sock);
				else if(ERROR_VALUE==ECONNABORTED) 
					lprintf(LOG_WARNING,"%04d DATA Connection aborted by peer, receiving on socket %d"
						,xfer.ctrl_sock,*xfer.data_sock);
					lprintf(LOG_WARNING,"%04d !DATA ERROR %d receiving on data socket %d"
						,xfer.ctrl_sock,ERROR_VALUE,*xfer.data_sock);
deuce's avatar
deuce committed
				sockprintf(xfer.ctrl_sock,sess,"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);
deuce's avatar
deuce committed
			sockprintf(xfer.ctrl_sock,sess,"451 Unexpected socket error: %d",rd);
			error=TRUE;
			break;
		}
		fwrite(buf,1,rd,fp);
		total+=rd;
		*xfer.lastactive=time(NULL);
deuce's avatar
deuce committed
	ftp_close_socket(xfer.data_sock,xfer.data_sess,__LINE__);
	if(error && startup->options&FTP_OPT_DEBUG_DATA)
		lprintf(LOG_DEBUG,"%04d DATA socket %d closed",xfer.ctrl_sock,*xfer.data_sock);
	if(xfer.filepos+total < startup->min_fsize) {
		lprintf(LOG_WARNING,"%04d DATA received %lu bytes for %s, less than minimum required (%lu bytes)"
			,xfer.ctrl_sock, xfer.filepos+total, xfer.filename, startup->min_fsize);
deuce's avatar
deuce committed
		sockprintf(xfer.ctrl_sock,sess,"550 File size less than minimum required (%lu bytes)"
		error=TRUE;
	}
	if(error) {
		if(!xfer.append)
			ftp_remove(xfer.ctrl_sock, __LINE__, xfer.filename);
	} else {
		dur=(long)(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);
			f.dir=xfer.dir;
			filedat=getfileixb(&scfg,&f);
			if(scfg.dir[f.dir]->misc&DIR_AONLY)  /* Forced anonymous */
				f.misc|=FM_ANON;
			f.cdt=flength(xfer.filename);
			/* Description specified with DESC command? */
			if(xfer.desc!=NULL && *xfer.desc!=0)	
				SAFECOPY(f.desc,xfer.desc);
			/* Necessary for DIR and LIB ARS keyword support in subsequent chk_ar()'s */
			SAFECOPY(xfer.user->curdir, scfg.dir[f.dir]->code);

			/* FILE_ID.DIZ support */
			p=strrchr(f.name,'.');
			if(p!=NULL && scfg.dir[f.dir]->misc&DIR_DIZ) {
				for(i=0;i<scfg.total_fextrs;i++)
rswindell's avatar
rswindell committed
						&& 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);
						ftp_remove(xfer.ctrl_sock, __LINE__, 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);
						sprintf(tmp,"%sDESC.SDI",scfg.temp_dir);
							ftp_remove(xfer.ctrl_sock, __LINE__, 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 */
							SAFECOPY(desc,ext);
							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;
							SAFECOPY(f.desc,desc+i); 
						ftp_remove(xfer.ctrl_sock, __LINE__, 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.desc,getfname(xfer.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);
deuce's avatar
deuce committed
		sockprintf(xfer.ctrl_sock,sess,"226 Upload complete (%lu cps).",cps);
deuce's avatar
deuce committed
	if(ftp_set!=NULL && !terminate_server)
deuce's avatar
deuce committed
static BOOL start_tls(SOCKET *sock, CRYPT_SESSION *sess, BOOL resp)
{
	BOOL nodelay;
	ulong nb;
	int status;
	char *estr;

	if (get_ssl_cert(&scfg, NULL) == -1) {
		lprintf(LOG_ERR, "Unable to get certificate");
		if (resp)
			sockprintf(*sock, *sess, "431 TLS not available");
deuce's avatar
deuce committed
		return FALSE;
	}
	if (cryptCreateSession(sess, CRYPT_UNUSED, CRYPT_SESSION_SSL_SERVER) != CRYPT_OK) {
		lprintf(LOG_ERR, "Unable to create TLS session");
		if (resp)
			sockprintf(*sock, *sess, "431 TLS not available");
deuce's avatar
deuce committed
		return FALSE;
	}
	if (cryptSetAttribute(*sess, CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_CERTVERIFY) != CRYPT_OK) {
		lprintf(LOG_ERR, "Unable to disable certificate verification");
		cryptDestroySession(*sess);
		*sess = -1;
		if(resp)
			sockprintf(*sock, *sess, "431 TLS not available");
deuce's avatar
deuce committed
		return FALSE;
	}
	if (cryptSetAttribute(*sess, CRYPT_SESSINFO_PRIVATEKEY, scfg.tls_certificate) != CRYPT_OK) {
		lprintf(LOG_ERR, "Unable to set private key");
		cryptDestroySession(*sess);
		*sess = -1;
		if (resp)
			sockprintf(*sock, *sess, "431 TLS not available");
deuce's avatar
deuce committed
		return FALSE;
	}
	nodelay = TRUE;
	setsockopt(*sock,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
	nb=0;
	ioctlsocket(*sock,FIONBIO,&nb);
	if (cryptSetAttribute(*sess, CRYPT_SESSINFO_NETWORKSOCKET, *sock) != CRYPT_OK) {
		lprintf(LOG_ERR, "Unable to set network socket");
		cryptDestroySession(*sess);
		*sess = -1;
		if (resp)
			sockprintf(*sock, *sess, "431 TLS not available");
deuce's avatar
deuce committed
		return TRUE;
	}
	if (resp)
		sockprintf(*sock, -1, "234 Ready to start TLS");
	if ((status = cryptSetAttribute(*sess, CRYPT_SESSINFO_ACTIVE, 1)) != CRYPT_OK) {
		estr = get_crypt_error(*sess);
		lprintf(LOG_ERR, "Unable to set session active (%d:%s)", status, estr);
		free_crypt_attrstr(estr);
		return TRUE;
	}
	if (startup->max_inactivity) {
		if (cryptSetAttribute(*sess, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity) != CRYPT_OK) {
			lprintf(LOG_ERR, "Unable to set max inactivity");
			return TRUE;
		}
	}
	return FALSE;
}
deuce's avatar
deuce committed
static void filexfer(union xp_sockaddr* addr, SOCKET ctrl_sock, CRYPT_SESSION ctrl_sess, SOCKET pasv_sock, CRYPT_SESSION pasv_sess, SOCKET* data_sock
					,CRYPT_SESSION *data_sess, char* filename, long filepos, BOOL* inprogress, BOOL* aborted
					,BOOL delfile, BOOL tmpfile
					,time_t* lastactive
					,user_t* user
rswindell's avatar
rswindell committed
					,client_t* client
					,int dir
					,BOOL receiving
					,BOOL credits
					,BOOL append
deuce's avatar
deuce committed
					,char* desc,BOOL protected)
	socklen_t	addr_len;
deuce's avatar
deuce committed
	union xp_sockaddr	server_addr;
	struct timeval	tv;
	fd_set			socket_set;
deuce's avatar
deuce committed
	char		host_ip[INET6_ADDRSTRLEN];

	if((*inprogress)==TRUE) {
		lprintf(LOG_WARNING,"%04d !TRANSFER already in progress",ctrl_sock);
deuce's avatar
deuce committed
		sockprintf(ctrl_sock,ctrl_sess,"425 Transfer already in progress.");
		if(tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES))
			ftp_remove(ctrl_sock, __LINE__, filename);