char *SIGN_stls_c="{FILESIGN=stls.c:20090121161130+0900:1f47280fb4b415e3:Author@DeleGate.ORG:Fck3G6NtMSbdhy99CJw7bevG9dZOAG8POj/DTwH+Brw90hVILK0KXbyMVdTkawjMdG98SG86b0lmEWE1dcyl4tsOdP3Is1JkQRS0lHv0xs6/4SsX+vYH8qUXgBocM3jDq+7vceokfACMmsFvKmoizEc2wp44bffthy5I4V2kJco=}";

/*////////////////////////////////////////////////////////////////////////
Copyright (c) 2004-2008 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:	stls.c (STARTTLS)
Author:		Yutaka Sato <ysato@delegate.org>
Description:
	STARTTLS

	RFC2487 SMTP
	RFC2595 IMAP and POP3
	RFC2449 POP3 (CAPA STLS)
	RFC2228,RFC4217 FTP
	(RFC2817 HTTP)

History:
	041223	created
//////////////////////////////////////////////////////////////////////#*/
#include "delegate.h"
#include "http.h" /* OREQ_MSG */
#include "fpoll.h"
int isinSSL(int fd);
static int withSTLS_FCL = 0;
static int withSTLS_FSV = 0;

static int STLS_opt = 0;
static double STLS_implicit_wait = 0.25;
static int STLS_wait_set = 0;
static double STLS_implicit_waitSV = 1.0;

extern int SSLready;
extern int SSLstart;
int TIMEOUT_STLS = 10*1000;

void scan_STLS(Connection *Conn,PCStr(stls)){
	CStr(filt,1024);
	CStr(proto,1024);
	CStr(cmapb,1024);
	CStr(map,1024);
	CStr(opt1,1024);
	const char *cmap;
	const char *fp;
	refQStr(np,filt);
	refQStr(op,opt1);
	int fcl = 0;
	int fsv = 0;
	int opt = 0;
	const char *com = "sslway";
	CStr(comb,1024);

	/*
	cmap = wordscanY(stls,AVStr(filt),sizeof(filt),"^:");
	if( *cmap == ':' )
		cmap++;
	cmap = wordscanY(cmap,AVStr(proto),sizeof(proto),"^:");
	if( proto[0] )
		sprintf(cmapb,"starttls/{%s}%s",proto,cmap);
	else	sprintf(cmapb,"starttls%s",cmap);
	*/
	strcpy(filt,"");
	strcpy(proto,"");
	strcpy(map,"");
	scan_Listlist(stls,':',AVStr(filt),AVStr(proto),AVStr(map),VStrNULL,VStrNULL);
	if( proto[0] )
		/*
		sprintf(cmapb,"starttls/{%s}%s%s",proto,*map?":":"",map);
		*/
		sprintf(cmapb,"starttls//{%s}%s%s",proto,*map?":":"",map);
	else	sprintf(cmapb,"starttls%s%s",*map?":":"",map);

	for( fp = filt; *fp;){
		int optL;
		np = wordscanY(fp,AVStr(opt1),sizeof(opt1),"^,");
		optL = 0;

		if( strncaseeq(opt1,"-im",3) ){
			STLS_implicit_wait = -1;
			STLS_wait_set = 1;
			goto NEXT;
		}
		if( strncaseeq(opt1,"im",2) ){
			STLS_implicit_wait = Scan_period(opt1+2,'s',0);
			STLS_wait_set = 1;
			if( STLS_implicit_wait && STLS_implicit_wait < 0.001 )
				STLS_implicit_wait = 0.001;
			goto NEXT;
		}
		if( *opt1 == '-' ){
			ovstrcpy(opt1,opt1+1);
			optL |= PF_STLS_OPT;
		}
		if( op = strchr(opt1,'/') ){
			setVStrPtrInc(op,0);
			if( strncaseeq(op,"ssl",3) ){
				optL |= PF_STLS_SSL;
			}
		}
		if( strcaseeq(opt1,"opt") ){
			opt = PF_STLS_OPT;
		}else
		if( strcaseeq(opt1,"ssl") ){
			opt = PF_STLS_SSL;
		}else
		if( strcaseeq(opt1,"mim") || strcaseeq(opt1,"mitm") ){
			fsv = PF_STLS_DO | opt | optL | PF_MITM_DO;
			fcl = PF_STLS_DO | opt | optL | PF_MITM_DO;
		}else
		if( strcaseeq(opt1,"FSV") || strcaseeq(opt1,"SV") ){
			fsv = PF_STLS_DO | opt | optL;
			if( strneq(op,"im",2) ) fsv |= PF_SSL_IMPLICIT;
		}else
		if( strcaseeq(opt1,"FCL") || strcaseeq(opt1,"CL") ){
			fcl = PF_STLS_DO | opt | optL;
			if( strneq(op,"im",2) ) fcl |= PF_SSL_IMPLICIT;
		}else{
			syslog_ERROR("FILTER[%s]: %s\n",com,fp);
			com = fp;
			if( 1 < num_ListElems(com,':') ){
				sprintf(comb,"{%s}",com);
				com = comb;
			}
			break;
		}
	NEXT:
		fp = np;
		if( *fp == ',' )
			fp++;
	}
	if( fsv & PF_STLS_DO ){
		withSTLS_FSV++;
		sprintf(map,"%s:FSV:%s",com,cmapb);
		if( fsv & PF_MITM_DO  ) Strins(AVStr(map),"--mitm,");
		if( fsv & PF_STLS_SSL ) Strins(AVStr(map),"-ss,");
		if( fsv & PF_SSL_IMPLICIT ) Strins(AVStr(map),"--im,");
		if( fsv & PF_STLS_OPT ) Strins(AVStr(map),"-o,");
		scan_CMAP(Conn,map);
		sv1log("STLS -> CMAP=\"%s\"\n",map);
	}
	if( fcl & PF_STLS_DO ){
		withSTLS_FCL++;
		sprintf(map,"%s:FCL:%s",com,cmapb);
		if( fcl & PF_MITM_DO  ) Strins(AVStr(map),"--mitm,");
		if( fcl & PF_STLS_SSL ) Strins(AVStr(map),"-ss,");
		if( fcl & PF_SSL_IMPLICIT ) Strins(AVStr(map),"--im,");
		if( fcl & PF_STLS_OPT ) Strins(AVStr(map),"-o,");
		scan_CMAP(Conn,map);
		sv1log("STLS -> CMAP=\"%s\"\n",map);
	}
}

int checkWithSTLS(Connection *Conn,PCStr(what),PCStr(proto),PCStr(user)){
	CStr(filter,1024);
	int flags;

	if( streq(what,"FSV") && withSTLS_FSV == 0 ){
		return 0;
	}
	if( streq(what,"FCL") && withSTLS_FCL == 0 ){
		return 0;
	}
	if( withFilter(Conn,what,"starttls",proto,user,AVStr(filter)) ){
		if( streq(what,"FSV") ) /* 0 <= ToS */
		if( (ServerFlags & PF_STLS_CHECKED) == 0 ){
			VA_gethostNAME(ToS,&Conn->sv_sockHOST);
		}
		flags = PF_STLS_CHECKED
		      | PF_STLS_DO
		      | (isinList(filter,"--mitm") ? PF_MITM_DO : 0)
		      | (strneq(filter,"--im",4) ? PF_SSL_IMPLICIT : 0)
		      | ((strncmp(filter,"-o,",3) == 0) ? PF_STLS_OPT : 0)
		      | ((strncmp(filter,"-ss,",4) == 0) ? PF_STLS_SSL : 0);

		if( streq(what,"FSV") ){
			/* disable SSL with SV if CL is not with SSL */
			if( flags & PF_MITM_DO )
			if( ServerFlags & PF_IS_MASTER ){
				sv1log("MITM: suppressed STLS=fsv for MASTER\n");
				flags &= ~PF_STLS_DO;
			}else
			if( (ClientFlags & (PF_SSL_ON/*|PF_MITM_ON*/)) == 0 ){
				flags &= ~PF_STLS_DO;
	if( lTLS() )
	Verbose("MITM: suppressed STLS=fsv for non https client %X[%s]\n",
	ClientFlags,CLNT_PROTO);
			} 
		}
		if( streq(what,"FCL") )
			ClientFlags = (ClientFlags & ~PF_STLS_ON) | flags;
		else	ServerFlags = (ServerFlags & ~PF_STLS_ON) | flags;
		return 1;
	}else{
		flags = PF_STLS_DO|PF_STLS_OPT|PF_STLS_ON;
		if( streq(what,"FCL") )
			ClientFlags = PF_STLS_CHECKED | ClientFlags & ~flags;
		else	ServerFlags = PF_STLS_CHECKED | ServerFlags & ~flags;
		/*
		else	ServerFlags = PF_STLS_CHECKED | ClientFlags & ~flags;
		*/
		return 0;
	}
}
int withSTLS_SV(Connection *Conn){
	return ServerFlags & PF_STLS_ON;
}
int SSLtunnelNego(Connection *Conn,PCStr(host),int port,int sock);
static void setCFI_IDENT(Connection *Conn,int cid[2],int sv);
static void getCFI_IDENT(Connection *Conn,int cid[2],int sv);

static int nonSSL_SV(Connection *Conn){
	double Start;
	if( FromS <= 0 ){
		return 1;
	}
	Start = Time();
	if( 0 < PollIn(FromS,(int)(1000*STLS_implicit_waitSV)) ){
		IStr(buf,8);
		int rcc;

		if( !IsAlive(FromS) ){
			sv1log("-- nonSSL_SV ? [%d][%s] Disconnected (%.2f)\n",
				FromS,DST_PROTO,Time()-Start);
		}
		rcc = recvPeekTIMEOUT(FromS,AVStr(buf),sizeof(buf));
		if( 0 < rcc ){
			/* could be '2' in POP, '*' in IMAP, ... */
			sv1log("%d [%X %X %X]\n",rcc,buf[0],buf[1],buf[2]);
			return 1;
		}
	}
	return 0;
}
int willSTLS_SV(Connection *Conn){
	int oflags = ServerFlags;

	if( (ServerFlags & PF_STLS_CHECKED) == 0 ){
		checkWithSTLS(Conn,"FSV",REAL_PROTO,"");

		if( (ClientFlags & PF_MITM_ON)
		 && (ServerFlags & PF_STLS_OPT)
		){
			/* 9.8.0-pre1 STLS=mitm,-fsv
			 * expecting STLS=fsv in the upstream proxy
			 * as PROXY, MASTER, or SOCKS
			 */
			sv1log("-- STLS=mitm,-fsv %X %d,%d,%d\n",ServerFlags,
				toMaster,toProxy,ServViaSocks);
			ServerFlags |= PF_SSL_ON;
			return 0;
		}
		if( ServerFlags & (PF_SSL_ON|PF_STLS_ON) ){
		}else
		if( ServerFlags & PF_STLS_DO )
		if( strcaseeq(DST_PROTO,"http")
		 || strcaseeq(DST_PROTO,"https")
		 || strcaseeq(DST_PROTO,"sockmux")
		 || strcaseeq(DST_PROTO,"tcprelay")
		 || strcaseeq(DST_PROTO,"telnet")
		 || strcaseeq(DST_PROTO,"telnets")
		 || (ServerFlags & PF_SSL_IMPLICIT)
		 || strcaseeq(DST_PROTO,"imaps") && !nonSSL_SV(Conn)
		 || strcaseeq(DST_PROTO,"pop3s") && !nonSSL_SV(Conn)
		){	int fsv;

			if( toProxy
			 && streq(GatewayProto,"http")
			 && streq(DST_PROTO,"https")
			){
				/* SERVER=http + STLS=fsv + PROXY */
				SSLtunnelNego(Conn,DST_HOST,DST_PORT,ToS);
				sv1log("-- PROXY=%s:%d [%s] as SSLTUNNEL\n",
					GatewayHost,GatewayPort,GatewayProto);
			}

			fsv = insertFSVX(Conn,"starttls",DST_PROTO,FromC,ToS);
			if( 0 <= fsv ){
				if( strncaseeq(CLNT_PROTO,"ftp",3) ){
					if( ToSX == -1 ){
					ToSX = dup(ToS); /* for ServSock() */
					}
				}
				dup2(fsv,ToS);
				close(fsv);
				ServerFlags |= PF_STLS_ON;
			}
		}
	}
	sv1log("willSTLS_SV: ServerFlags=%X\n",ServerFlags);
	if((ServerFlags & PF_STLS_CAP) == 0 )
	if( ServerFlags & PF_STLS_DONTTRY )
	if( ServerFlags & PF_STLS_OPT ){
		sv1log("WILL NOT TRY STLS -- NOT SUPPORTED and OPTIONAL\n");
		return 0;
	}

	/* this should be applied to any protocols */
	if( streq(DST_PROTO,"smtps")
	 || streq(DST_PROTO,"imaps")
	)
	if( ServerFlags & PF_SSL_ON ){
		if( oflags & PF_SSL_ON ){
			sv1log("-- with SSL already -- %s://%s:%d\n",
				DST_PROTO,DST_HOST,DST_PORT);
		}
		return 0;
	}

	return ServerFlags & PF_STLS_DO;
}

int waitSTLS_CL(Connection *Conn,int timeout){
	if( (ClientFlags & PF_STLS_ON) == 0 )
	{
	double St,Sr,Ss,Sn;
	St = Time();
	if( 0 < PollIn(ClientSock,timeout) ){
		Sr = Time();
		if( isinSSL(ClientSock) ){
			CStr(sts,128);
			int fcl;
			Ss = Time();
			fcl = insertFCLX(Conn,"starttls",CLNT_PROTO,FromC,ToS);
			Sn = Time();
			syslog_ERROR("## STLS ## IMPLICIT SSL ON %d,%d,%d,%d\n",
				ClientSock,FromC,ToC,fcl);
			sprintf(sts,"%.2f %.2f/%.2f %.2f %.2f = %.2f",
				St-ClntConnTime,Sr-St,timeout/1000.0,
				Ss-Sr,Sn-Ss,
				Sn-ClntConnTime);
			sv1log("OK: SSL/cl %s\n",sts);
			if( 0 <= fcl ){
				if( strncaseeq(CLNT_PROTO,"ftp",3) ){
					if( FromC == ClientSock ){ 
						ClientSock = dup(ClientSock);
					}
				}
				dup2(fcl,FromC);
				close(fcl);
				ClientFlags |= PF_STLS_ON;
				return 1;
			}
		}
	}
	}
	return 0;
}
int willSTLS_CL(Connection *Conn){
	if( Conn->from_myself ){
		sv1log("## STLS ## no client %X [%d/%d/%d] %X %s:%d\n",
			Conn->from_myself,ClientSock,FromC,ToC,
			ClientFlags,Client_Host,Client_Port);
		return 0;
	}
	if( (ClientFlags & PF_STLS_CHECKED) == 0 ){
		CTX_pushClientInfo(Conn);
		checkWithSTLS(Conn,"FCL",CLNT_PROTO,"");

		if( (ClientFlags & PF_STLS_OPT) == 0 )
		if( ClientFlags & PF_STLS_DO )
		if( 0 < STLS_implicit_wait )
		if( STLS_wait_set == 0 )
		if( STLS_implicit_wait < 10 )
		if( strcaseeq(CLNT_PROTO,"tcprelay")
		 || strcaseeq(CLNT_PROTO,"https")
		){
			waitSTLS_CL(Conn,(int)((8-STLS_implicit_wait)*1000));
		}

		if( ClientFlags & PF_STLS_DO )
		if( 0 < STLS_implicit_wait )
			waitSTLS_CL(Conn,(int)(STLS_implicit_wait*1000));
		/*
		if( 0 < PollIn(ClientSock,(int)(STLS_implicit_wait*1000)) )
		if( isinSSL(ClientSock) ){
			int fcl;
			fcl = insertFCLX(Conn,"starttls",CLNT_PROTO,FromC,ToS);
			syslog_ERROR("## STLS ## IMPLICIT SSL ON %d,%d,%d,%d\n",
				ClientSock,FromC,ToC,fcl);
			if( 0 <= fcl ){
				dup2(fcl,FromC);
				close(fcl);
				ClientFlags |= PF_STLS_ON;
			}
		}
		*/
                HL_popClientInfo(); 
	}
	if( ClientFlags & PF_STLS_ON )
		return 0;
	return ClientFlags & PF_STLS_DO;
}
/*
 * needSTLS() -- TLS must be started before going ahead
 */
int needSTLS(Connection *Conn){
        if( willSTLS_CL(Conn) )
        if( (ClientFlags & PF_STLS_ON) == 0 )
	if( (ClientFlags & PF_STLS_OPT) == 0 )
	if( (ClientFlags & PF_MITM_DO) == 0 )
		return 1;
	return 0;
}
int needSTLS_SV(Connection *Conn){
        if( willSTLS_SV(Conn) )
        if( (ServerFlags & PF_STLS_ON) == 0 )
	if( (ServerFlags & PF_STLS_OPT) == 0 )
		return 1;
	return 0;
}
/* needSTLS_SV() may insert a SSL filter using ToS */
int needSTLS_SVi(Connection *Conn,int server){
	int doSSL;
	int toS;

	toS = ToS;
	ToS = server;
	doSSL = needSTLS_SV(Conn);
	ToS = toS;
	return doSSL;
}
int insertTLS_CL(Connection *Conn,int client,int server){
	int fcl = -1;
	if( willSTLS_CL(Conn) ){
		fcl = insertFCLX(Conn,"starttls",CLNT_PROTO,client,server);
		if( 0 <= fcl ){
			ClientFlags |= PF_STLS_ON;
		}
	}
	return fcl;
}
int insertTLS_SV(Connection *Conn,int client,int server){
	int fsv = -1;
	if( willSTLS_SV(Conn) ){
		fsv = insertFSVX(Conn,"starttls",REAL_PROTO,client,server);
		if( 0 <= fsv ){
			ServerFlags |= PF_STLS_ON;
		}
	}
	return fsv;
}

#include "filter.h"
int HTTP_auth2ident(Connection *Conn,PCStr(auth),AuthInfo *ident,int decomp);
int getServ(Connection *Conn);

static void setCFI_IDENT(Connection *Conn,int cid[2],int sv){
	CStr(env,1024);

	pipe(cid);
	sprintf(env,"CFI_IDENT=%d",cid[1]);
	putenv(stralloc(env));
}
static void getCFI_IDENT(Connection *Conn,int cid[2],int sv){
	double St = Time();
	IStr(ids,1024);
	IStr(ident,128);
	int rcc;

	putenv("CFI_IDENT=-1");
	if( PollIn(cid[0],1000) ){
		rcc = read(cid[0],ids,sizeof(ids)-1);
		if( 0 < rcc ){
			setVStrEnd(ids,rcc);
			getFieldValue2(ids,"Ident",AVStr(ident),sizeof(ident));
			sv1log("%s-Ident: %.3f %.3f %d <%s>\n",sv?"SV":"CL",
				Time()-ServConnTime,Time()-St,rcc,ident);
		}
	}
	close(cid[0]);
	close(cid[1]);
}

int beManInTheMiddle(Connection *Conn,FILE *tcp,FILE *fcp);
int CTX_moved_url_to(DGC*ctx,PCStr(myhostport),PCStr(method),PVStr(url));
static int moved(Connection *Conn,FILE *tc,int fd,PCStr(host),int port){
	CStr(hp,1024);
	CStr(url,1024);
	sprintf(hp,"%s:%d",host,port);
	sprintf(url,"https://%s",hp);
	if( CTX_moved_url_to(Conn,hp,"CONNECT",AVStr(url)) ){
		return 1;
	}
	return 0;
}

int HTTP_STARTTLS_withCL(Connection *Conn,FILE *fc,FILE *tc){
/*
	double sws = 0;
	if( strcaseeq(CLNT_PROTO,"https") ){
		if( STLS_implicit_wait < 0.5 ){
			sws = STLS_implicit_wait;
			STLS_implicit_wait = 0.5;
		}
	}
*/
	if( Conn->from_myself ){
		return 0;
	}
	if( willSTLS_CL(Conn) ){
		if( 0 < READYCC(fc) ){
			/* pipelined request */
		}else
		if( 0 < fPollIn(fc,10*1000) ){
			if( ClientFlags & PF_MITM_DO ){
				beManInTheMiddle(Conn,tc,fc);
				if( ClientFlags & PF_SSL_ON ){
					return 1;
				}
			}
			if( isinSSL(fileno(fc)) ){
				int fcl;
				fcl = insertTLS_CL(Conn,FromC,ToS);
				if( fcl < 0 ){
					sv1log("### SSL/cl failure %X %X\n",
						ClientFlags,ServerFlags);
				}else{
				dup2(fcl,ClientSock);
				close(fcl);
				}
				return 1;
			}
		}
	}
	return 0;
}


#define lfprintf SMTP_lfprintf
void SMTP_lfprintf(FILE *log,FILE *tosc,PCStr(fmt),...);
void SMTP_putserv(FILE *log,FILE *fs,FILE *ts,PVStr(resp),PCStr(fmt),...);

int SMTP_STARTTLS_withCL(Connection *Conn,FILE *fc,FILE *tc){
	CStr(stat,1024);
	int fcl;

	if( willSTLS_CL(Conn) ){
		SSLstart = 1;
		fcl = insertTLS_CL(Conn,FromC,ToS);
		SSLstart = 0;
		if( 0 <= fcl ){
			lfprintf(NULL,tc,"220 Ready to start TLS\r\n");
			fflush(tc);
			fflush(fc);

			/* wait the completion of SSL negotiation and
			 * initialization in SSLway thread for safety ...
			 */
			PollIn(fcl,TIMEOUT_STLS);

			dup2(fcl,fileno(tc));
			dup2(fcl,fileno(fc));
			close(fcl);
			return 1;
		}
	}
	lfprintf(NULL,tc,"454 A Don't start TLS\r\n");
	fflush(tc);
	return 0;
}
int SMTP_STARTTLS_withSV(Connection *Conn,FILE *ts,FILE *fs){
	CStr(stat,1024);
	int fsv;

	if( willSTLS_SV(Conn) ){
		SMTP_putserv(NULL,fs,ts,AVStr(stat),"STARTTLS\r\n");
		if( stat[0] != '2' ){
			goto NOTLS;
		}
		fsv = insertTLS_SV(Conn,FromC,ToS);
		if( fsv < 0 ){
			goto NOTLS;
		}
		dup2(fsv,fileno(ts));
		dup2(fsv,fileno(fs));
		close(fsv);
	}
	return 0;
NOTLS:
	ServerFlags &= ~PF_STLS_ON;
	if( (ServerFlags & PF_STLS_OPT) == 0 )
		return -1;
	else	return 0;
}

int POP_STARTTLS_withCL(Connection *Conn,FILE *fc,FILE *tc,PCStr(com),PCStr(arg)){
/*
	if( strcaseeq(com,"CAPA") ){
		if( needSTLS(Conn) ){
			fputs("+OK Capability list follows\r\n",tc);
			fputs("STLS\r\n",tc);
			fputs(".\r\n",tc);
			return 1;
		}
	}
*/
	if( strcaseeq(com,"STLS") ){
		if( willSTLS_CL(Conn) ){
			int fcl;
			SSLstart = 1;
			fcl = insertTLS_CL(Conn,ToC,ToS);
			SSLstart = 0;
			if( 0 <= fcl ){
				fputs("+OK\r\n",tc);
				fflush(tc);

				PollIn(fcl,TIMEOUT_STLS);

				dup2(fcl,fileno(tc));
				dup2(fcl,fileno(fc));
				close(fcl);
			}else{
				fputs("-ERR\r\n",tc);
			}
			return 1;
		}
	}
	return 0;
}
int POP_STARTTLS_withSV(Connection *Conn,FILE *ts,FILE *fs,PCStr(user)){
	CStr(resp,1024);

	if( ServerFlags & (PF_SSL_ON|PF_STLS_ON) ){
		return 1;
	}
	if( willSTLS_SV(Conn) ){
		if( strcaseeq(DST_PROTO,"pop3s") ){
			return -3;
		}
		sv1log("POP D-S: STLS to server\n");
		fputs("STLS\r\n",ts);
		fflush(ts);
		if( fgets(resp,sizeof(resp),fs) == NULL )
			return -1;
		sv1log("POP S-D: %s",resp);
		if( *resp == '+' ){
			int fsv;
			fsv = insertTLS_SV(Conn,ToC,ToS);
			if( 0 <= fsv ){
				dup2(fsv,fileno(ts));
				dup2(fsv,fileno(fs));
				close(fsv);
				return 0;
			}
		}else{
			return -2;
		}
	}
	return 0;
}

int IMAP_STARTTLS_withCL(Connection *Conn,FILE *fc,FILE *tc,PCStr(tag),PCStr(com),PCStr(arg)){
	if( willSTLS_CL(Conn) == 0 )
		return 0;

	if( strcaseeq(com,"STARTTLS") ){
		int fcl;
		SSLstart = 1;
		fcl = insertTLS_CL(Conn,ToC,ToS);
		SSLstart = 0;
		if( 0 <= fcl ){
			fprintf(tc,"%s OK Begin TLS negotiation\r\n",tag);
			fflush(tc);

			PollIn(fcl,TIMEOUT_STLS);

			dup2(fcl,fileno(tc));
			dup2(fcl,fileno(fc));
			close(fcl);
		}else{
			fprintf(tc,"%s BAD\r\n",tag);
		}
		return 1;
	}
	return 0;
}
int IMAP_STARTTLS_withSV(Connection *Conn,FILE *ts,FILE *fs,PCStr(user)){
	CStr(resp,1024);
	CStr(code,1024);

	if( willSTLS_SV(Conn) ){
		fputs("stls0 STARTTLS\r\n",ts);
		fflush(ts);
		if( fgets(resp,sizeof(resp),fs) == NULL )
			return -1;
		*code = 0;
		Xsscanf(resp,"%*s %s",AVStr(code));

		if( strcaseeq(code,"OK") ){
			int fsv;
			fsv = insertTLS_SV(Conn,ToC,ToS);
			if( 0 <= fsv ){
				dup2(fsv,fileno(ts));
				dup2(fsv,fileno(fs));
				close(fsv);
				return 0;
			}
		}else{
			return -2;
		}
	}
	return 0;
}

int FTP_putSTLS_FEAT(Connection *Conn,FILE *tc,int wrap){
	if( (ClientFlags & PF_STLS_DO) )
	if( (ClientFlags & (PF_STLS_ON|PF_SSL_ON)) == 0 )
	{
		if( wrap ) fprintf(tc,"211-Extensions supported\r\n");
		fprintf(tc," AUTH TLS\r\n");
		fprintf(tc," PBSZ\r\n");
		fprintf(tc," PROT\r\n");
		if( wrap ) fprintf(tc,"211 END\r\n");
		return 1;
	}
	return 0;
}

#define comeq(com1,com2) (strcasecmp(com1,com2) == 0)
int FTP_STARTTLS_withCL(Connection *Conn,FILE *tc,FILE *fc,PCStr(com),PCStr(arg)){
	if( comeq(com,"AUTH") ){
		sv1log("#### %s %s\n",com,arg);
		if( comeq(arg,"TLS") || comeq(arg,"SSL") ){
			int fcl;
			if( willSTLS_CL(Conn) )
			{
				SSLstart = 1;
				fcl = insertTLS_CL(Conn,ToC,ToS);
				SSLstart = 0;
			}
			else	fcl = -1;
			if( 0 <= fcl ){
				ClientSock = dup(ClientSock);
				fprintf(tc,"234 OK\r\n");
				fflush(tc);
				fflush(fc);

				PollIn(fcl,TIMEOUT_STLS);

				dup2(fcl,fileno(tc));
				dup2(fcl,fileno(fc));
				close(fcl); /* 9.5.7 bug */
				/* this bug disables disconnection from
				 * the server-side to terminate session
				 */
				return 1;
			}else{
				fprintf(tc,"500 not supported\r\n");
				return 1;
			}
		}
	}else
	if( comeq(com,"PBSZ") ){
		sv1log("#### %s %s\n",com,arg);
		fprintf(tc,"200 OK\r\n");
		return 1;
	}else
	if( comeq(com,"PROT") ){
		sv1log("#### %s %s\n",com,arg);
		fprintf(tc,"200 OK\r\n");
		return 1;
	}
	else
	if( comeq(com,"FEAT") ){
		if( (ClientFlags & PF_STLS_DO) )
		if( (ClientFlags & PF_STLS_OPT) == 0 )
		if( FTP_putSTLS_FEAT(Conn,tc,1) )
			return 1;
	}else
	if( comeq(com,"QUIT")
	){
		return 0;
	}else
	if( needSTLS(Conn) ){
		sv1log("#### needAUTH, rejected %s %s\n",com,arg);
		fprintf(tc,"534 do AUTH first.\r\n");
		fflush(tc);
		return 1;
	}
	return 0;
}
int FTP_STARTTLS_withSV(Connection *Conn,FILE *ts,FILE *fs){
	if( 0 <= ToS )
	if( willSTLS_SV(Conn) ){
		CStr(resp,128);

		if( ServerFlags & PF_SSL_IMPLICIT ){ /* ftps://serv */
			if( ServerFlags & PF_STLS_ON ){
				return 1;
			}
			strcpy(resp,"200\r\n");
		}else{
		if( ServerFlags & PF_STLS_SSL )
			fprintf(ts,"AUTH SSL\r\n");
		else	fprintf(ts,"AUTH TLS\r\n");
		fflush(ts);
		fgets(resp,sizeof(resp),fs);
		}
		
		if( *resp == '2' ){
			int fsv;
			fsv = insertTLS_SV(Conn,ToC,ToS);
			if( 0 <= fsv ){
				ToSX = dup(ToS); /* for ServSock() */
				dup2(fsv,fileno(ts));
				dup2(fsv,fileno(fs));
				close(fsv);

				if( (ServerFlags & PF_STLS_SSL) == 0 ){
					/* AUTH TLS */
					if( *resp == '2' ){
						fprintf(ts,"PBSZ 0\r\n");
						fflush(ts);
						fgets(resp,sizeof(resp),fs);
					}
					if( *resp == '2' ){
						fprintf(ts,"PROT P\r\n");
						fflush(ts);
						fgets(resp,sizeof(resp),fs);
					}
				}
				return 1;
			}
		}
		if( (ServerFlags & PF_STLS_OPT) == 0 ){
			return -1;
		}
	}
	return 0;
}
int FTP_dataSTLS_FCL(Connection *Conn,Connection *dataConn,int cldata)
{	int fcldata;

	if( (ClientFlags & PF_STLS_ON) == 0 ){
		return -1;
	}
	dataConn->cl.p_flags = ClientFlags & ~PF_STLS_ON;
	fcldata = insertTLS_CL(dataConn,cldata,-1);
	return fcldata;
}
int FTP_dataSTLS_FSV(Connection *Conn,Connection *dataConn,int svdata)
{	int fsvdata;

	if( (ServerFlags & PF_STLS_ON) == 0 ){
		return -1;
	}
	dataConn->sv.p_flags = ServerFlags & ~PF_STLS_ON;
	SSLstart = 1;
	fsvdata = insertTLS_SV(dataConn,-1,svdata);
	SSLstart = 0;
	if( 0 <= fsvdata ){
		/* should wait command/stat on the control connection ? */
		/* PollIn(FromS,TIMEOUT_STLS); */

		dup2(fsvdata,svdata);
		close(fsvdata);
		/* 9.5.7 bug: this disalbes sending ABOR over SSL
		ToS = svdata;
		*/
		dataConn->sv.p_wfd = svdata;
		return 1;
	}
	return -1;
}

#include <ctype.h>
int STLS_error(Connection *Conn,int fromC){
	int fail = 0;
	int nready;
	char statb[2];
	/*
	char stat;
	*/
	int stat;
	FILE *fst;
	double Start;
	int ni;

	if( ClientFlags & PF_STLS_ON ){
		/* SSLway is active already */
		return 0;
	}

	Start = Time();
	pipe(CFI_SYNC);
	FromC = fromC;

	if( STLS_wait_set == 0 ){
		if( streq("https",CLNT_PROTO) ){
			STLS_implicit_wait = 2;
		}else
		if( isinList("tcprelay",CLNT_PROTO) ){
			STLS_implicit_wait = 1;
		}else
		if( isinList("ftps,telnets,pop3s,imaps,nntps,ldaps",
			CLNT_PROTO) ){
			STLS_implicit_wait = 2;
		}else
		if( isinList("socks",CLNT_PROTO) ){
			STLS_implicit_wait = 1;
		}
	}
	if( needSTLS(Conn) ){
		/*
		daemonlog("E","ERROR: SSL/cl is not detected\n");
		*/
		daemonlog("E","ERROR: SSL/cl is not detected (%.2f %.2f)\n",
			Start-ClntConnTime,Time()-ClntConnTime);
		fail = -1;
		goto EEXIT;
	}
	if( (ClientFlags & PF_STLS_ON) == 0 ){
		/* no sslway is invoked */
		goto EEXIT;
	}

	close(CFI_SYNC[1]); CFI_SYNC[1] = 0;
	fst = fdopen(CFI_SYNC[0],"r");
	statb[0] = statb[1] = stat = 0;
	for(;;){
		nready = fPollIn(fst,1000);
		if( nready == 0 )
		if( stat == 0 || stat == 'W' ){
			sv1log("waiting CFI_SYNC from sslway (%d)...\n",300);
			nready = fPollIn(fst,300*1000);
		}
		if( 0 < nready )
			stat = getc(fst);
		else	stat = -2;
		statb[0] = stat;
		if( isalnum(stat) ){
			sv1log("%.3f CFI_SYNC ready=%d [%X/%s]\n",
				Time()-Start,nready,stat,statb);
		}else{
			sv1log("%.3f CFI_SYNC ready=%d [%X]\n",
				Time()-Start,nready,stat);
		}
		if( nready <= 0 || stat < 0 || stat == EOF )
			break;
		if( stat == '\n' )
			break;
	}
	if( nready <= 0 || stat < 0 || stat == EOF || !IsAlive(fromC) ){
		/*
		daemonlog("E","ERROR: SSL/cl disconnected\n");
		*/
		daemonlog("E","ERROR: SSL/cl disconnected: %d %X %d[%d]\n",
			nready,stat,IsAlive(fromC),fromC);
		fail = -2;
	}
	goto EXIT;
EEXIT:
	close(CFI_SYNC[1]); CFI_SYNC[1] = 0;
EXIT:
	close(CFI_SYNC[0]); CFI_SYNC[0] = 0;
	FromC = -1;
	return fail;
}

/* 050427 extracted from sslway.c */
int SSL_isrecord(int fd,int timeout);
int isinSSL(int fd)
{	unsigned CStr(buf,6); /**/
	int rcc,leng,type,vmaj,vmin;
	int isready,rd;

	isready = inputReady(fd,&rd);
	if( isready <= 0 ){
		return 0;
	}

	if( SSL_isrecord(fd,1) ){
		/* more strict checking of SSL/TLS */
		return 1;
	}else{
		/*
		return 0;
		*/
	}

	buf[0] = 0x7F;
	rcc =
	recvPeekTIMEOUT(fd,AVStr(buf),1);
	if( (buf[0] & 0x80) || buf[0] < 0x20 ){
		syslog_ERROR("isinSSL ? [%X] from client\n",0xFF&buf[0]);
		if( buf[0] == 0x80 ){
			rcc = recvPeekTIMEOUT(fd,AVStr(buf),5);
			syslog_ERROR("SSL Hello?%d [%X %d %d %d %d]\n",rcc,
				buf[0],buf[1],buf[2],buf[3],buf[4]);
			leng = (0x7F&buf[0]) << 8 | buf[1];
			type = buf[2];
			if( type == 1 ){ /* SSLv3 ClientHello */
				vmaj = buf[3];
				vmin = buf[4];
				return 1;
			}
		}
		else
		if( buf[0] == 22 ){ /* ConentType:handshake */
			rcc = recvPeekTIMEOUT(fd,AVStr(buf),sizeof(buf));
			syslog_ERROR("SSL Hello?%d [%X %d %d %d %d]\n",rcc,
				buf[0],buf[1],buf[2],buf[3]<<8|buf[4],buf[5]);
			if( buf[5] == 1 ){
				return 1;
			}
		}
	}
	return 0;
}

int getServPorts(int sc,int sv[]);
int optionalAdminPortSSL(int port);
int defineAdminSTLS(Connection *Conn){
	int admsock;
	int admport = 0;
	const char *host;
	CStr(stls,128);
	refQStr(sp,stls);
	int sv[8],nports;

	nports = getServPorts(8,sv);
	admsock = withAdminPort(&host,&admport);
	if( admport ){
		if( nports == 1 ){
			setVStrPtrInc(sp,'-');
		}
		else
		if( optionalAdminPortSSL(admport) ){
			setVStrPtrInc(sp,'-');
		}
		sprintf(sp,"fcl:*:*:");
		sp += strlen(sp);
		if( *host )
			sprintf(sp,"-P{%s:%d}",host,admport);
		else	sprintf(sp,"-P%d",admport);
		sp += strlen(sp);
		sv1log("Generated default STLS=%s\n",stls);
		DELEGATE_pushEnv("STLS",stls);
	}
	return 0;
}

/* '"DIGEST-OFF"' */
        
