/*////////////////////////////////////////////////////////////////////////
Copyright (c) 2005-2006 National Institute of Advanced Industrial Science and Technology (AIST)

Permission to use this material for noncommercial and/or evaluation
purpose, copy this material for your own use, and distribute the copies
via publicly accessible on-line media, without fee, is hereby granted
provided that the above copyright notice and this permission notice
appear in all copies.
AIST MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY OF THIS
MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS", WITHOUT ANY EXPRESS
OR IMPLIED WARRANTIES.
/////////////////////////////////////////////////////////////////////////
Content-Type:	program/C; charset=US-ASCII
Program:	sftp.c (ftp client to sftp server gateway)
Author:		Yutaka Sato <ysato@delegate.org>
Description:
	PROBLEM
		uploading huge file (STOR)
		 ... getting into local tmpfile is bad
History:
	050728	created
//////////////////////////////////////////////////////////////////////#*/
#include "vsocket.h"
#include "dglib.h"
#include "fpoll.h"
#include "proc.h"
#include "vsignal.h"

#include "log.h"
static int ErrorResp = 0;
#define DEBUG	(lGATEWAY()==0 && ErrorResp==0)?0:sv1log
#define Error	sv1log

#if defined(__KURO_BOX__)
#include <pty.h>
#define Forkpty(pty,name) forkpty(pty,name,NULL,NULL)
#else
int Forkpty(int *pty,char *name);
#endif
int Stty(int fd,const char *mode);

int CC_connect(PCStr(proto),PCStr(host),int port,PCStr(user));
int CC_accept(PCStr(proto),PCStr(host),int port,PCStr(user),int fromS);
int Mkfifo(PCStr(path),int mode);
char *fgetsTO(PVStr(b),int z,FILE *f,int t1,int t2);
FILE *fopenTO(PCStr(path),PCStr(mode),int timeout);
FileSize file_copy(FILE *src,FILE *dst,FILE *cache,FileSize bytes,int *binary);

static int DATA_MAX = 10*1024*1024;
static char *sftp_com = "sftp -oPort=%d %s@%s";
static int pid;
int SFTP_WITH_SEEK = 1;

static void putresp(FILE *tc,int code,PCStr(stat),...){
	CStr(resp,1024);
	refQStr(rp,resp);
	VARGS(8,stat);

	sprintf(rp,"%d ",code); rp += strlen(rp);
	sprintf(rp,stat,VA8);
	fprintf(tc,"%s\r\n",resp);
	fflush(tc);
	DEBUG("--SFTPGW >> %s\n",resp);
}
static void putsftp(FILE *ts,PCStr(fmt),...){
	CStr(com,1024);
	VARGS(8,fmt);

	sprintf(com,fmt,VA8);
	fprintf(ts,"%s\r\n",com);
	fflush(ts);
	DEBUG("--SFTP << %s\r\n",com);
}

/*
 * newer sftp forces ECHO unconditionally, thus the response for the
 * "ls ..." command includes "ls ..." before and "sftp> \r\n" after
 * the list data.
 */
static void relay_list(FILE *fs,int dsock,PCStr(scom)){
	CStr(res,1024);
	int tout;
	FILE *tc;

	tout = 3*1000;
	tc = fdopen(dsock,"w");
	for(;;){
		if( fgetsTO(AVStr(res),sizeof(res),fs,tout,1) == NULL ){
			break;
		}
		if( strtailstr(res,"sftp> ") ){
			break;
		}
		if( *res == '\r' || *res == '\n'
		 || strheadstrX(res,"sftp> ",0)
		 || strheadstrX(res,scom,0)
		){
			continue;
		}
		fputs(res,tc);
	}
	fflush(tc);
	fcloseFILE(tc);
}

static void relay_resp(FILE *fs,int dsock,int timeout,PVStr(resp),PCStr(com),int dolog){
	CStr(res,1024);
	FILE *fp;
	refQStr(rp,resp);
	refQStr(dp,res);
	int tout0;
	int tout;

	if( 0 < timeout )
		tout0 = tout = timeout;
	else	tout0 = tout = 3*1000;

	if( resp )
		setVStrEnd(resp,0);
	if( fPollIn(fs,tout) <= 0 ){
		DEBUG("--SFTP >> response timeout [%s]%d\n",com,tout);
		return;
	}
	if( 0 <= dsock )
		fp = fdopen(dsock,"w");
	else	fp = NULL;

	for(;;){
		if( 0 < fgetsBuffered(AVStr(res),sizeof(res),fs) ){
		}else
		if( fgetsTO(AVStr(res),sizeof(res),fs,tout,1) == NULL ){
			break;
		}
		if( *res == '@' ){
			dolog = 1;
			ErrorResp = 1;
		}
		if( dolog ){
			if( strchr(res,'\n') )
				DEBUG("--SFTP >>(%s) %s",com,res);
			else	DEBUG("--SFTP >>(%s/NO-NL)[%s]\n",com,res);
		}
		if( strtailstr(res,"sftp> ") ){
			if( rp ){
if( lGATEWAY() )
sv1log("####@@@@ resp[%s][%s]\n",res,resp);
				strcpy(rp,res);
				rp += strlen(rp);
			}
			if( fPollIn(fs,10) <= 0 )
				break;
		}
		if( dp = strpbrk(res,"\r\n") )
			strcpy(dp,"\r\n");
		if( fp != NULL )
			fputs(res,fp);
		if( streq(res,"\r\n") || streq(res,"\n") ){
			/* ignore empty lines */
		}else
		if( rp ){
			strcpy(rp,res);
			rp += strlen(rp);
		}
		if( resp && *resp == 0 ){
			tout = tout0;
		}else
		if( strtailstr(res,":")
		 || strtailstr(res,": ")
		 || strtailstr(res,"? ")
		){
			tout = 1;
		}else
		if( *res == '\r' || *res == '\n' )
			tout = 100;
		else	tout = tout0;
	}
	if( fp != NULL )
		fflush(fp);
	ErrorResp = 0;
}

static void closeAll(){
	int fi;
	int rcode;
	int lfd = curLogFd();
	for( fi = 3; fi < 256; fi++ ){
		if( fi == lfd )
			continue;
		rcode = close(fi);
		/*
		if( rcode == 0 ) DEBUG("-- closed %d\n",fi);
		*/
	}
}

static int SftpPid;
static int SftpPty = -1;

int dupLogFd();
void execsystem(PCStr(what),PCStr(pathcom));
/*
static int forkSftp(PCStr(host),int port,PCStr(user),PCStr(pass),int tf[2]){
*/
static int forkSftp(PCStr(host),int port,PCStr(user),PCStr(pass),int tf[2],PVStr(rresp)){
	CStr(com,1024);
	CStr(resp,0x10000);
	int pty;
	IStr(name,128);
	FILE *fs;
	FILE *ts;
	int to[2];
	int from[2];
	int rcode;

	if( curLogFd() == fileno(stderr) ){
		dupLogFd(); /* 9.8.2 not to send log-output to pty */
	}
	pid = Forkpty(&pty,(char*)name);
	if( 0 < pid ){
		SftpPid = pid;
		SftpPty = pty;
		DEBUG("--SFTP: pid=%d pty master %d %s\n",pid,pty,name);
		tf[0] = pty;
		tf[1] = pty;

		msleep(100);
		fs = fdopen(pty,"r");
		relay_resp(fs,-1,15*1000,AVStr(resp),"FORK-1",1);

		if( strstr(resp,"Are you sure you want to continue") ){
			sprintf(com,"yes\n");
			write(pty,com,strlen(com));
			DEBUG("--SFTP: answered yes for...\n%s\n",resp);
			relay_resp(fs,-1,15*1000,AVStr(resp),"FORK-2",1);
		}

		sprintf(com,"%s\r\n",pass);
		write(pty,com,strlen(com));
		DEBUG("--SFTP: sent the password...\n");
		relay_resp(fs,-1,15*1000,AVStr(resp),"FORK-3",1);

		if( strtailstr(resp,"Password:")
		 || strtailstr(resp,"Password: ")
		 || strtailstr(resp,"password: ")
		 || strstr(resp,"Enter passphrase for key")
		){
			DEBUG("--SFTP %s\n",resp);
if( lGATEWAY() )
sv1log("####@@@@ KILL sftp pid=%d\n",pid);
			Kill(SftpPid,SIGTERM);
			strcpy(rresp,resp);
			return -1;
		}

		sprintf(com,"progress\r\n");
		write(pty,com,strlen(com));
		relay_resp(fs,-1,5*1000,AVStr(resp),"FORK-4",1);

		return 0;
	}

	sprintf(com,"[%s]","sftp");
	Xsprintf(TVStr(com),sftp_com,port,user,host);
	DEBUG("--SFTP: %s\n",com);
if( lGATEWAY() )
sv1log("####@@@@ command %s\n",com);

	if( pid == 0 ){
		closeAll();
		Stty(0,"-echo");
		/*
		system("stty raw");
		*/
		/*
		rcode = system(com);
		printf("ssh exit(%d)\n",rcode);
		*/
		execsystem("sftp",com);
		printf("ssh exec(%s) failed\n",com);
		_exit(0);
	}

	pipe(to);
	pipe(from);
	if( Fork("Sftp") == 0 ){
		close(to[1]);
		dup2(to[0],0);
		close(from[0]);
		dup2(from[1],1);
		closeAll();
		rcode = system(com);
		printf("ssh exit(%d)\n",rcode);
		_exit(0);
	}
	close(to[0]);
	close(from[1]);
	tf[0] = from[0];
	tf[1] = to[1];
	fs = fdopen(from[0],"r");
	relay_resp(fs,-1,20*1000,AVStr(resp),"FORK-1",1);
	return 0;
}

static int frelayB(FILE *src,FILE *dst){
	CStr(buf,8*1024);
	int rcc,wcc;

	if( ready_cc(src) ){
		rcc = fgetsBuffered(AVStr(buf),sizeof(buf),src);
	}else{
		rcc = read(fileno(src),buf,sizeof(buf));
	}
	if( 0 < rcc ){
		wcc = fwrite(buf,1,rcc,dst);
		if( wcc != rcc ){
			sv1log("--SFTP frelayB failed %d / %d\n",wcc,rcc);
			return -1;
		}
	}
	return rcc;
}
int SFTP_DATA_TIMEOUT = 10;
/*
static int relay_fifo(FILE *fs,FILE *tc,PCStr(fifo),int dsock,PCStr(com)){
*/
static int relay_fifo(FILE *fs,FILE *tc,PCStr(fifo),int dsock,PCStr(com),PVStr(resp)){
	FILE *sfp;
	FILE *dfp;
	refQStr(rp,resp);
	/*
	int fv[2];
	*/
	FILE *fv[2];
	int rv[2];
	int nready;
	int done = 0;
	int leng = 0;

	truncVStr(resp);
	sfp = fopenTO(fifo,"r",1);
if( lGATEWAY() )
sv1log("----FIFO recv OPEN %X %s\n",sfp,fifo);
	DEBUG("--SFTP relay [%s] %X\n",fifo,sfp);
	if( sfp == NULL ){
		/*
		relay_resp(fs,-1,1,VStrNULL,"RESP-A",1);
		*/
		int rcode;
		rcode = unlink(fifo);
		sv1log("----FIFO unlink(%s)=%d\n",fifo,rcode);
		while( 0 < fPollIn(fs,10) ){
			relay_resp(fs,-1,1,AVStr(rp),com,1);
			rp += strlen(rp);
		}
		return -1;
	}

	/*
	fv[0] = fileno(fs);
	fv[1] = fileno(sfp);
	*/
	fv[0] = fs;
	fv[1] = sfp;
	dfp = 0;

	for(;;){
		/*
		nready = PollIns(1000,2,fv,rv);
		*/
		/*
		nready = fPollIns(1000,2,fv,rv);
		*/
		if( done ){
			/* FreeBSD-select() does not detect the EOF of FIFO ? */
			nready = fPollIns(1,2,fv,rv);
if( lGATEWAY() )
sv1log("####@@@@ FIFO done nready=%d poll=%d\n",nready,fPollIn(sfp,1));
			if( nready == 0 ){
				nready = 1;
				rv[0] = 0;
				rv[1] = 1;
			}
		}else
		nready = fPollIns(SFTP_DATA_TIMEOUT*1000,2,fv,rv);
		DEBUG("--SFTP poll [%s] %d[%d %d]\n",fifo,nready,rv[0],rv[1]);
		if( nready <= 0 ){
			DEBUG("--SFTP NO RESP-B\n");
			break;
		}else{
			if( 0 < rv[0] ){
				IStr(res,1024);
				/*
				relay_resp(fs,-1,1,VStrNULL,com,1);
				*/
				relay_resp(fs,-1,1,AVStr(res),com,1);
				strcpy(rp,res);
				rp += strlen(res);
				if( !strcasestr(res,"sftp>") && res[0] != 0 ){
					/* 9.6.1 not the prompt implying fin. */
					if( strtailchr(res) != '\n' )
						strcat(res,"\n");
					DEBUG("--SFTP relay: %s",res);
				}else
				{
				unlink(fifo); /* notify recv. completion */
				DEBUG("--SFTP recv. seems complete\n");
					done = 1;
				}
			}
			if( 0 < rv[1] ){
				if( dfp == NULL ){
					dfp = fdopen(dsock,"w");
					if( dfp ){
						putresp(tc,150,"Ok");
					}
				}
				if( dfp ){
					int bytes;
					bytes = frelayB(sfp,dfp);
if( lGATEWAY() )
DEBUG("####@@@@ bytes=%d errno=%d\n",bytes,errno);
					DEBUG("--SFTP %d downloaded\n",bytes);
					if( bytes <= 0 ){
					DEBUG("--SFTP EOF=%d\n",feof(sfp));
						break;
					}
					leng += bytes;
				}
				/*
				dfp = fdopen(dsock,"w");
				if( dfp ){
					int isbin;
					int bytes;
					DEBUG("--SFTP relay [%s]...\n",fifo);
					putresp(tc,150,"Ok");
					bytes=file_copy(sfp,dfp,NULL,0,&isbin);
					fcloseFILE(dfp);
					DEBUG("--SFTP bin=%d,%d downloaded\n",
						isbin,bytes);
				}
				break;
				*/
			}
		}
	}
	fclose(sfp);
	if( dfp )
		fcloseFILE(dfp);

if( lGATEWAY() )
sv1log("----FIFO recv DONE %d\n",leng);
	return leng;
	/*
	return 0;
	*/
}

int file_size(int fd);
int File_size(PCStr(path));
int File_is(PCStr(path));
/*
int relayFile2Fifo(FILE *rfp,PCStr(regfile),PCStr(fifo)){
*/
int relayFile2Fifo(FILE *fs,FILE *rfp,PCStr(regfile),PCStr(fifo)){
	FILE *ffp;
	CStr(buf,8*1024);
	int siz,rem,rcc,rcc1;
	int start;
	int last = time(0);

	ffp = fopen(fifo,"w+");
if( lGATEWAY() )
sv1log("----FIFO relay OPEN %X %s\n",ffp,fifo);
	if( rfp == 0 || ffp == 0 )
		return -1;

	rcc = 0;
	start = time(0);
	for(;;){
		siz = File_size(regfile);
		if( siz < 0 ){
			break;
		}
		rem = siz - ftell(rfp);

		if( lVERB() )
		DEBUG("--SFTP-DATA rem[%d] = %d (%d - %d)\n",
			fileno(rfp),rem,ftell(rfp),File_size(regfile));

		if( 0 < rem ){
			rcc1 = fread(buf,1,sizeof(buf),rfp);
			if( rcc1 <= 0 )
				break;
			rcc += rcc1;
			fwrite(buf,1,rcc1,ffp);
			last = time(0);
		}else{
			if( !File_is(fifo) ){
				/* fifo is unlinked on recv. complete */
				break;
			}
			fflush(ffp);
			if( 10 < time(0)-start ){
				if( fPollIn(fs,1) == 0
				 //&& IsAlive(fileno(fifo))
				 && time(0)-last < SFTP_DATA_TIMEOUT ){
					/* no prompt for the next command yet */
				}else
				break;
			}
			msleep(100);
		}
	}
	fflush(ffp);
	fclose(ffp);
if( lGATEWAY() )
sv1log("####@@@@ FIFO relay DONE %d %s\n",rcc,fifo);
	return rcc;
}

unsigned int trand1(unsigned int max);
static FILE *getfile(PVStr(path),PCStr(mode),int fifo){
	CStr(npath,1024);
	int max = 64;
	FILE *fp;
	int fi;
	int fx;

	sprintf(path,"/tmp/sftpgw.%d.%d",getpid(),time(NULL));
	if( fifo ){
		if( Mkfifo(path,0600) != 0 ){
			DEBUG("--SFTP cant create FIFO: %s\n",path);
			return 0;
		}
		fp = NULL;
	}else{
		if( (fp = fopen(path,mode)) == 0 ){
			DEBUG("--SFTP cant create FILE: %s\n",path);
			return 0;
		}
	}
	fx = trand1(max);
	for( fi = 0; fi < max; fi++ ){
		sprintf(npath,"/tmp/sftpgw.%02x",fx);
		if( rename(path,npath) == 0 ){
			DEBUG("--SFTP TEMP FILE: %s\n",npath);
			strcpy(path,npath);
			return fp;
		}
	}
	DEBUG("--SFTP ERROR: can't rename %s\n",path);
	return fp;
}

const char *linehead(PCStr(str),PCStr(pat),int igncase){
	const char *sp,*np;
	for( sp = str; sp; sp = np ){
		if( strheadstrX(sp,pat,igncase) ){
			return sp;
		}
		if( np = strchr(sp,'\n') ){
			np++;
		}
	}
	return 0;
}
int scanresp(PCStr(sresp),PCStr(head),PVStr(value),int siz){
	const char *sp;
	if( sp = linehead(sresp,head,0) ){
		linescanX(sp+strlen(head),BVStr(value),siz);
		return 1;
	}
	return 0;
} 

static char *BadPath = "No such file or directory";

static void SftpGW(PCStr(host),int port,int gw){
	int tofrom_sftp[2];
	FILE *fc;
	FILE *tc;
	FILE *ts;
	FILE *fs;
	int rcc;
	CStr(req,1024);
	CStr(com,1024);
	CStr(arg,1024);
	CStr(scom,1024);
	CStr(user,128);
	CStr(passMD5,128);
	CStr(md5,1024);
	CStr(logindir,1024);
	CStr(rnfr,512);
	CStr(res,1024);
	CStr(xcom,1024);
	const char *dp;
	int vsock = -1;
	int dsock = -1;
	CStr(fifo,1024);
	CStr(sresp,0x10000);
	CStr(path,1024);
	CStr(resp1,1024);
	int Ok;
	int Bad;
	int xpid;
	int nready;
	int nfc;
	FILE *fpv[2];
	int rdv[2];
	int ncc = 0;
	int leng;

	DEBUG("--SFTPGW start\n");
	fc = fdopen(gw,"r");
	tc = fdopen(gw,"w");
	putresp(tc,220,"SFTP/FTP gateway ready.");
	fflush(tc);
	ts = NULL;
	fs = NULL;

	logindir[0] = 0;
	fpv[0] = fc;
	nfc = 1;
	for(;;){
		nready = fPollIns(0,nfc,fpv,rdv);
		if( nready == 0 && errno == EINTR ){
			DEBUG("--SFTPGW ignored EINTR\n");
			sleep(1);
			continue;
		}
		if( nready <= 0 ){
			break;
		}
		if( 1 < nfc && 0 < rdv[1] ){
			DEBUG("--SFTPGW EOF from server?\n");
			relay_resp(fs,-1,0,AVStr(sresp),"",1);
			break;
		}

		fflush(tc);
		if( fgets(req,sizeof(req),fc) == 0 )
			break;
		dp = wordScan(req,com);
		if( *dp == ' ' ){
			textScan(dp+1,arg);
		}else
		lineScan(dp,arg);
		if( strcaseeq(com,"PASS") )
			DEBUG("--SFTPGW << [%s][****]\n",com);
		else	DEBUG("--SFTPGW << [%s][%s]\n",com,arg);

		if( strcaseeq(com,"QUIT") ){
			putresp(tc,221,"Ok Bye.");
			fclose(fc);
			fclose(tc);
			fc = 0;
			tc = 0;
			if( fs == NULL ){
				DEBUG("--SFTPGW DONE (not started)\n");
				break;
			}
			gw = CC_accept("sftp",host,port,user,fileno(fs));
			if( 0 <= gw ){
				fc = fdopen(gw,"r");
				fpv[0] = fc;
				tc = fdopen(gw,"w");
				ncc++;
				DEBUG("--SFTPGW SFTPCC restart #%d\n",ncc);
				putresp(tc,220,"Ok (reusing)");
				continue;
			}
			DEBUG("--SFTPGW DONE\n");
			break;
		}else
		if( strcaseeq(com,"NOOP") ){
			putresp(tc,200,"Ok");
		}else
		if( strcaseeq(com,"USER") ){
			strcpy(user,arg);
			putresp(tc,331,"Send password or passphrase for '%s'",
				arg);
		}else
		if( strcaseeq(com,"PASS") ){
			if( ts != NULL ){
				toMD5(arg,md5);
				DEBUG("--SFTP reusing user[%s]pass[%s][%s]\n",
					user,passMD5,md5);
				if( !streq(md5,passMD5) ){
					putresp(tc,530,"No");
					continue;
				}else	putresp(tc,230,"Ok");
				putsftp(ts,"cd %s",logindir);
				relay_resp(fs,-1,0,AVStr(sresp),"restart",1);
				continue;
			}
			/*
			if( forkSftp(host,port,user,arg,tofrom_sftp) != 0 ){
			*/
			if( forkSftp(host,port,user,arg,tofrom_sftp,AVStr(sresp)) != 0 ){
				IStr(prompt,1024);
				lineScan(sresp,prompt);
				DEBUG("--SFTP login failed\n");
				/*
				putresp(tc,530,"No (Login failed)");
				*/
				putresp(tc,530,"No (Login failed:'%s')",prompt);
if( lGATEWAY() )
sv1log("####@@@@ LOGIN FAILURE\n");
continue; /* to return normal resp. for following commands... */
				break;
			}
			toMD5(arg,passMD5);
			fs = fdopen(tofrom_sftp[0],"r");
			ts = fdopen(tofrom_sftp[1],"w");
			fpv[1] = fs;
			nfc = 2;
			putresp(tc,230,"Ok");

			putsftp(ts,"pwd");
			relay_resp(fs,-1,0,AVStr(sresp),com,1);
			/*
			if( strneq(sresp,"Remote working directory: ",26) ){
				CStr(path,1024);
				lineScan(sresp+26,path);
			*/
			if( scanresp(sresp,"Remote working directory:",
			    AVStr(path),sizeof(path)) ){
				strcpy(logindir,path);
			}
		}else
		if( strcaseeq(com,"TYPE") ){
			putresp(tc,200,"Ok");
		}else
		if( strcaseeq(com,"PASV") ){
			VSAddr ba;
			int balen;
			CStr(mport,128);
			vsock = server_open("SftpGW",VStrNULL,0,1);
			if( 0 <= vsock ){
				balen = sizeof(ba);
				getsockname(vsock,(SAP)&ba,&balen);
#if defined(__CYGWIN__)
				if( VSA_addrisANY(&ba) ){
				    /*connection to 0.0.0.0 fails on CYGWIN?*/
				    VSA_atosa(&ba,VSA_port(&ba),"127.0.0.1");
				}
#endif
				VSA_prftp(&ba,AVStr(mport));
			putresp(tc,227,"Enterning Passive Mode (%s)",mport);
			}else{
				putresp(tc,500,"No");
			}
		}else
		if( ts == NULL ){
			sv1log("Not Logged In: %s",req);
			putresp(tc,530,"Please login with USER and PASS.");
		}else
		if( strcaseeq(com,"PWD") ){
			putsftp(ts,"pwd");
			relay_resp(fs,-1,0,AVStr(sresp),com,1);
			/*
			if( strneq(sresp,"Remote working directory: ",26) ){
				CStr(path,1024);
				lineScan(sresp+26,path);
			*/
			if( scanresp(sresp,"Remote working directory:",
			    AVStr(path),sizeof(path)) ){
				putresp(tc,257,"\"%s\"",path);
			}else{
				putresp(tc,257,"\"/\"");
			}
		}else
		if( strcaseeq(com,"CWD") ){
			putsftp(ts,"cd %s",arg);
			relay_resp(fs,-1,0,AVStr(sresp),com,1);
			/*
			if( strneq(sresp,"Couldn't",8)
			 || strneq(sresp,"Can't",4) ){
			*/
			if( linehead(sresp,"Couldn't",0)
			 || linehead(sresp,"Can't",0) ){
				if( strstr(sresp,BadPath) )
					putresp(tc,550,BadPath);
				else
				putresp(tc,550,"No");
			}else	putresp(tc,250,"Ok");
		}else
		/*
		if( strcaseeq(com,"SIZE") ){
			putsftp(ts,"ls -ld %s",arg);
			relay_resp(fs,-1,0,AVStr(sresp),com,1);
			if( *sresp == '-' ){
				int sz = 0;
				sscanf(sresp,"%*s %*s %*s %*s %d",&sz);
				putresp(tc,213,"%d",sz);
			}else{
				putresp(tc,550,"Not a plain file");
			}
		}else
		*/
		if( strcaseeq(com,"STOR") ){
			if( vsock < 0 ){
				putresp(tc,500,"No");
				continue;
			}
			dsock = ACCEPT(vsock,1,-1,10);
			if( 0 <= dsock ){
				CStr(tmpf,1024);
				FILE *dfp;
				FILE *tmp;
				int bytes;
				int isbin;

				dfp = fdopen(dsock,"r");
				if( dfp == NULL ){
					putresp(tc,500,"No-1");
					continue;
				}
				tmp = getfile(AVStr(tmpf),"w",0);
				if( tmp == NULL ){
					putresp(tc,500,"No-2");
					fclose(dfp);
					continue;
				}
				putresp(tc,150,"Ok");
				bytes = file_copy(dfp,tmp,NULL,DATA_MAX,&isbin);
				DEBUG("--SFTPGW bin=%d,%d bytes upload\n",
					isbin,bytes);
				fclose(tmp);
				fclose(dfp);

				putsftp(ts,"put %s %s",tmpf,arg);
				relay_resp(fs,-1,15*1000,AVStr(sresp),com,1);
				if( strncmp(sresp,"Could",5) == 0 )
					putresp(tc,550,"No");
				else	putresp(tc,226,"Ok");
				unlink(tmpf);
				dsock = -1;
			}else{
				putresp(tc,500,"accept error");
			}
			vsock = -1;
		}else
		if( strcaseeq(com,"RETR") ){
			if( vsock < 0 ){
				putresp(tc,500,"No");
				continue;
			}
			dsock = ACCEPT(vsock,1,-1,10);
			if( arg[0]==0 ||streq(arg,".") ||strtailstr(arg,"/") ){
				putresp(tc,550,"RETR for dir [%s]",arg);
				if( 0 <= dsock ){
					close(dsock);
					dsock = -1;
				}
			}else
			if( 0 <= dsock ){
				CStr(rpath,1024);
				FILE *rfp;
				int pid;

				truncVStr(rpath);
				getfile(AVStr(fifo),NULL,1);
				if( SFTP_WITH_SEEK ){
					/* newer sftp with output seeking */
					sprintf(rpath,"%sr",fifo);
					rfp = fopen(rpath,"w+");
					if( rfp ){
					  pid = Fork("SFTP-DATA");
					  if( pid == 0 ){
						/*
						relayFile2Fifo(rfp,rpath,fifo);
						*/
						relayFile2Fifo(fs,rfp,rpath,fifo);
						_exit(0);
					  }
					}
					putsftp(ts,"get %s %s",arg,rpath);
				}else{
				putsftp(ts,"get %s %s",arg,fifo);
				}
				/*
				if( relay_fifo(fs,tc,fifo,dsock,com)==0 )
				*/
				leng = relay_fifo(fs,tc,fifo,dsock,com,AVStr(sresp));
				if( leng <= 0 ){
					sv1log("RETR ERROR: %s\n",sresp);
				}
				if( leng == 0 && strstr(sresp,BadPath) )
					putresp(tc,550,BadPath);
				else
				if( 0 < leng )
					putresp(tc,226,"Ok");
				else	putresp(tc,550,"No");
				relay_resp(fs,-1,1,AVStr(sresp),com,1);
				if( rpath[0] ){
					unlink(rpath);
					xpid = NoHangWait();
					DEBUG("--SFTP-DATA finished, pid=%d\n",
						xpid);
				}
				close(dsock);
				dsock = -1;
				unlink(fifo);
			}else{
				putresp(tc,500,"accept error");
			}
			vsock = -1;
		}else
		if( strcaseeq(com,"NLST") || strcaseeq(com,"LIST") ){
			if( vsock < 0 ){
				putresp(tc,500,"No");
				continue;
			}
			dsock = ACCEPT(vsock,1,-1,10);
			if( 0 <= dsock ){
				putresp(tc,150,"Ok");
				if( arg[0] == '-' ){
					char *op;
					for( op = arg+1; *op; ){
						if( strchr(" \t\r\n",*op) )
							break;
						if( strchr("L",*op) ){
							ovstrcpy(op,op+1);
						}else	op++;
					}
				}
				/*
				if( streq(com,"NLST") || strstr(arg,"-l") )
					putsftp(ts,"ls %s",arg);
				else	putsftp(ts,"ls -l %s",arg);
				relay_resp(fs,dsock,0,VStrNULL,com,0);
				*/
				if( streq(com,"NLST") || strstr(arg,"-l") )
					sprintf(scom,"ls %s",arg);
				else	sprintf(scom,"ls -l %s",arg);
				putsftp(ts,"%s",scom);
				relay_list(fs,dsock,scom);
				close(dsock);
				dsock = -1;
				putresp(tc,226,"Ok");
			}else{
				putresp(tc,500,"accept error");
			}
			vsock = -1;
		}else
		if( strcaseeq(com,"RNFR") ){
			strcpy(rnfr,arg);
			putresp(tc,350,"Ok");
		}else
		if( strcaseeq(com,"RNTO") ){
			putsftp(ts,"rename %s %s",rnfr,arg);
			relay_resp(fs,-1,0,AVStr(sresp),com,1);
			if( dp = strstr(sresp,"Couldn't") ){
				lineScan(dp,resp1);
				putresp(tc,550,"No %s",resp1);
			}else	putresp(tc,250,"Ok");
		}else
		if( strcaseeq(com,"MKD") || strcaseeq(com,"RMD")
		 || strcaseeq(com,"DELE")
		){ 
			Bad = 550;
			if( strcaseeq(com,"MKD") ){
				Ok = 257;
				putsftp(ts,"mkdir %s",arg);
			}else
			if( strcaseeq(com,"RMD") ){
				Ok = 250;
				putsftp(ts,"rmdir %s",arg);
			}else{
				Ok = 250;
				putsftp(ts,"rm %s",arg);
			}
			relay_resp(fs,-1,0,AVStr(sresp),com,1);
			if( dp = strstr(sresp,"Couldn't") ){
				lineScan(dp,resp1);
				putresp(tc,Bad,"No %s",resp1);
			}else	putresp(tc,Ok,"Ok");
		}else
		{
			putresp(tc,502,"No not supported");
		}
	}
	if( fs != NULL && !feof(fs) ){
		putsftp(ts,"quit");
		relay_resp(fs,-1,0,AVStr(sresp),com,1);
	}
	xpid = NoHangWait();
	DEBUG("--SFTPGW finished, pid=%d\n",xpid);
}
static void sigTERM(int sig){
	int xpid;
	if( SftpPid ){
		if( 0 <= SftpPty ) close(SftpPty);
		msleep(10);
		Kill(SftpPid,SIGTERM);
		msleep(100);
		xpid = NoHangWait();
		sv1log("got SIGTERM/%d[%d] fin %d/%d\n",sig,SftpPid,SftpPty,
			xpid);
		SftpPty = -1;
		SftpPid = 0;
	}
	/* should salvage CC-socket ... */
	_Finish(0);
}
int connectToSftp(const char *host,int port,const char *user,int fdc,int fdv[]){
	int socks[2];

	socks[1] = CC_connect("sftp",host,port,user);
	if( 0 <= socks[1] ){
		DEBUG("---- SFTPCC HIT[%d] %s@%s:%d\n",socks[1],user,host,port);
		return socks[1];
	}
	DEBUG("---- SFTPCC MISS %s@%s:%d\n",user,host,port);
	Socketpair(socks);
	if( Fork("SftpGW") == 0 ){
		int fi;
		for( fi = 0; fi < fdc; fi++ )
			close(fdv[fi]);
		close(socks[1]);
		closeServPorts();
		signal(SIGINT,sigTERM);
		signal(SIGTERM,sigTERM);
		SftpGW(host,port,socks[0]);
		_exit(0);
	}
	close(socks[0]);
	return socks[1];
}
