/*
   3APA3A simpliest proxy server
   (c) 2002-2003 by ZARAZA <3APA3A@security.nnov.ru>

   please read License Agreement

   $Id: proxy.c,v 1.4 2003/10/29 14:40:05 vlad Exp $
*/


#include "proxy.h"
#ifndef PORT
#define PORT 3128
#endif

#define RETURN(xxx) { param->res = xxx; goto CLEANRET; }

#define child proxychild

#define SERVICE S_PROXY

char * proxy_stringtable[] = {
	"HTTP/1.0 400 Bad Request\r\n"
	"Proxy-Connection: close\r\n"
	"Content-type: text/html; unsigned charset=us-ascii\r\n"
	"\r\n"
	"<html><head><title>400 Bad Request</title></head>\r\n"
	"<body><h2>400 Bad Request</h2></body></html>\r\n",

	"HTTP/1.0 502 Bad Gateway\r\n"
	"Proxy-Connection: close\r\n"
	"Content-type: text/html; unsigned charset=us-ascii\r\n"
	"\r\n"
	"<html><head><title>502 Bad Gateway</title></head>\r\n"
	"<body><h2>502 Bad Gateway</h2><h3>Host Not Found or connection failed</h3></body></html>\r\n",

	"HTTP/1.0 503 Service Unavailable\r\n"
	"Proxy-Connection: close\r\n"
	"Content-type: text/html; unsigned charset=us-ascii\r\n"
	"\r\n"
	"<html><head><title>503 Service Unavailable</title></head>\r\n"
	"<body><h2>503 Service Unavailable</h2><h3>You have exceeded your traffic limit</h3></body></html>\r\n",

	"HTTP/1.0 503 Service Unavailable\r\n"
	"Proxy-Connection: close\r\n"
	"Content-type: text/html; unsigned charset=us-ascii\r\n"
	"\r\n"
	"<html><head><title>503 Service Unavailable</title></head>\r\n"
	"<body><h2>503 Service Unavailable</h2><h3>Recursion detected</h3></body></html>\r\n",

	"HTTP/1.0 501 Not Implemented\r\n"
	"Proxy-Connection: close\r\n"
	"Content-type: text/html; unsigned charset=us-ascii\r\n"
	"\r\n"
	"<html><head><title>501 Not Implemented</title></head>\r\n"
	"<body><h2>501 Not Implemented</h2><h3>Required action is not supported by proxy server</h3></body></html>\r\n",

	"HTTP/1.0 502 Bad Gateway\r\n"
	"Proxy-Connection: close\r\n"
	"Content-type: text/html; unsigned charset=us-ascii\r\n"
	"\r\n"
	"<html><head><title>502 Bad Gateway</title></head>\r\n"
	"<body><h2>502 Bad Gateway</h2><h3>Failed to connect parent proxy</h3></body></html>\r\n",

	"HTTP/1.0 500 Internal Error\r\n"
	"Proxy-Connection: close\r\n"
	"Content-type: text/html; unsigned charset=us-ascii\r\n"
	"\r\n"
	"<html><head><title>500 Internal Error</title></head>\r\n"
	"<body><h2>500 Internal Error</h2><h3>Internal proxy error during processing your request</h3></body></html>\r\n",

	"HTTP/1.0 407 Proxy Authentication Required\r\n"
	"Proxy-Authenticate: Basic realm=\"proxy\"\r\n"
	"Proxy-Connection: close\r\n"
	"Content-type: text/html; unsigned charset=us-ascii\r\n"
	"\r\n"
	"<html><head><title>407 Proxy Authentication Required</title></head>\r\n"
	"<body><h2>407 Proxy Authentication Required</h2><h3>Access to requested resource disallowed by administrator or you need valid username/password to use this resource</h3></body></html>\r\n",

	"HTTP/1.0 200 Connection established\r\n\r\n",

	"HTTP/1.0 200 Connection established\r\n"
	"Content-Type: text/html\r\n\r\n",

	"HTTP/1.0 404 Not Found\r\n"
	"Proxy-Connection: close\r\n"
	"Content-type: text/html; unsigned charset=us-ascii\r\n"
	"\r\n"
	"<html><head><title>404 Not Found</title></head>\r\n"
	"<body><h2>404 Not Found</h2><h3>File not found</body></html>\r\n"
};

struct datatable proxy_table = {
	STRINGTABLE,
	sizeof(proxy_stringtable)/sizeof(unsigned char *),
	(void *)proxy_stringtable
};

#define BUFSIZE 4096
#define LINESIZE 2048

void decodeurl(unsigned char *s){
 unsigned char *d = s;
 unsigned u;

 while(*s){
	if(*s == '%' && ishex(s[1]) && ishex(s[2])){
		sscanf(s+1, "%2x", &u);
		if (u != '\r' && u != '\n') {
			if (u == '\"' || u == '\\') *d++ = '\\';
			else if (u == 255) *d++ = 255;
			*d++ = u;
		}
		s+=3;
	}
	else if(*s == '?') {
		break;
	}
	else {
		*d++ = *s++;
	}
 }
 *d = 0;
}

void file2url(unsigned char *sb, unsigned char *buf, unsigned bufsize, int * inbuf){
 for(; *sb; sb++){
	if((bufsize - *inbuf)<16)break;
	if(*sb=='\r'||*sb=='\n')continue;
	if(isallowed(*sb))buf[(*inbuf)++]=*sb;
	else if(*sb == '\"'){
		memcpy(buf+*inbuf, "%%5C%%22", 6);
		(*inbuf)+=6;
	}
	else {
		sprintf(buf+*inbuf, "%%%.2x", (unsigned)*sb);
		(*inbuf)+=3;
	}
 }
}


void * child(void * data) {
#define param ((struct clientparam*)data)
 int res=0, i=0;
 unsigned char* buf = NULL, *newbuf;
 unsigned int inbuf;
 unsigned bufsize, reqlen = 0;
 unsigned char *sb=NULL, *se=NULL, *sp=NULL, *req=NULL, *su=NULL, *ss = NULL;
 unsigned char *ftpbase=NULL;
 unsigned short port = 0;
 unsigned char username[256];
 int keepalive = 0;
 int contentlength = -1;
 int isconnect = 0;
 int transparent = 0;
 int redirect = 0;
 int prefix = 0, ckeepalive=0;
 int ftp = 0;
 fd_set fds;
 struct timeval tv = {0,0};
 SOCKET ftps;



 if(!(buf = malloc(BUFSIZE))) {RETURN(21);}
 bufsize = BUFSIZE;
for(;;){
 memset(buf, 0, BUFSIZE);
 inbuf = 0;
 i = sockgetline(param->clisock, buf, LINESIZE - 1, '\n', (keepalive)?timeouts[CONNECTION_S]:timeouts[STRING_S]);
 if(keepalive && i<=0) {
	RETURN(555);
 }
 if(req) {
	if(i<=prefix || strncasecmp((char *)buf, (char *)req, prefix)){
		ckeepalive = 0;
		if(param->remsock != INVALID_SOCKET){
			shutdown(param->remsock, SHUT_RDWR);
			close(param->remsock);
		}
		param->sins.sin_addr.s_addr = 0;
		param->remsock = INVALID_SOCKET;
	}
 	else if(ckeepalive && param->remsock!= INVALID_SOCKET){
		FD_ZERO(&fds);
		FD_SET(param->remsock, &fds);
		res = select (param->remsock+1, &fds, NULL, NULL, &tv);
		if(res) {
			ckeepalive = 0;
			if(param->remsock != INVALID_SOCKET){
				shutdown(param->remsock, SHUT_RDWR);
				close(param->remsock);
			}
			param->remsock = INVALID_SOCKET;
		}
	}
	free(req);
 }
 req = (unsigned char *)strdup((char *)buf);
 if(!req){RETURN(510);}
 if(i<10) {
	RETURN(511);
 }
 if(!strncasecmp((char *)buf, "CONNECT", 7))isconnect = 1;
 if ((sb=(unsigned char *)(unsigned char *)strchr((char *)buf, ' ')) == NULL) {RETURN(512);}
 ss = ++sb;
 if(!isconnect) {
	if (!strncasecmp((char *)sb, "http://", 7)) {
		sb += 7;
	}
	else if (!strncasecmp((char *)sb, "ftp://", 6)) {
		ftp = 1;
		sb += 6;
	}
	else if(*sb == '/') {
		transparent = 1;
	}
	else {
		RETURN (513);
	}
 }
 else {
	 if ((se=(unsigned char *)(unsigned char *)strchr((char *)sb, ' ')) == NULL || sb==se) {RETURN (514);}
	 *se = 0;
 }
 if(!transparent) {
	if(!isconnect) {
		if ((se=(unsigned char *)(unsigned char *)strchr((char *)sb, '/')) == NULL || sb==se) {RETURN (515);}
 		*se = 0;
	}
	prefix = se - buf;
	su = (unsigned char*)strchr((char *)sb, '@');
	sp = (unsigned char*)strchr((char *)sb, ':');
	if (su) {
		*su = 0;
		if(sp && sp < su) {
			*sp = 0;
			if(param->extpassword) free(param->extpassword);
			param->extpassword = strdup(sp+1);
			sp = (unsigned char*)strchr((char *)su+1, ':');
		}
		if(param->extusername) free(param->extusername);
		param->extusername = strdup(sb);
		sb = su + 1;
	}
	port = (ftp)?21:80;
	if(sp){
		*sp = 0;
		port = atoi((char *)sp+1);
	}
	if(!ckeepalive){
		if(param->hostname)free(param->hostname);
		param->hostname = strdup(sb);
		param->sins.sin_port = htons(port);
	}
	*se = '/';
	if(!isconnect)memcpy(ss, se, i - (se - sb) + 1);

 }
 reqlen = i = strlen((char *)buf);
 if(!strncasecmp((char *)buf, "CONNECT", 7))param->operation = HTTP_CONNECT;
 else if(!strncasecmp((char *)buf, "GET", 3))param->operation = (ftp)?FTP_GET:HTTP_GET;
 else if(!strncasecmp((char *)buf, "PUT", 3))param->operation = (ftp)?FTP_PUT:HTTP_PUT;
 else if(!strncasecmp((char *)buf, "POST", 4))param->operation = HTTP_POST;
 else param->operation = HTTP_OTHER;

 do {
	buf[inbuf+i]=0;
/*printf("Got: %s\n", buf+inbuf);*/
	if(i > 25 && (!strncasecmp((char *)(buf+inbuf), "proxy-authorization", 19))){
		sb = (unsigned char *)strchr((char *)(buf+inbuf), ':');
		if(!sb)continue;
		++sb;
		while(isspace(*sb))sb++;
		if(!*sb || strncasecmp((char *)sb, "basic", 5)){
			continue;
		}
		sb+=5;
		while(isspace(*sb))sb++;
		i = de64(sb, username, 255);
		if(i<=0)continue;
		username[i] = 0;
		sb = (unsigned char *)strchr((char *)username, ':');
		if(sb){
			*sb = 0;
			param->password = (unsigned char *)strdup((char *)sb+1);
		}
		param->username = (unsigned char *)strdup((char *)username);
		continue;
	}
	if(!isconnect && (
			(i> 25 && !transparent && !strncasecmp((char *)(buf+inbuf), "proxy-connection:", 17))
			||
			(i> 16 && transparent && (!strncasecmp((char *)(buf+inbuf), "connection:", 11)))
			)){
		sb = (unsigned char *)strchr((char *)(buf+inbuf), ':');
		if(!sb)continue;
		++sb;
		while(isspace(*sb))sb++;
		if(!strncasecmp((char *)sb,"keep-alive", 10))keepalive = 1;
		continue; 
	}
	if(transparent && i > 6 && !strncasecmp((char *)buf + inbuf, "Host:", 5)){
		sb = (unsigned char *)strchr((char *)(buf+inbuf), ':');
		if(!sb)continue;
		++sb;
		while(isspace(*sb))sb++;
		se = (unsigned char *)strchr((char *)sb, '\r');
		if(!se) continue;
		if(se) *se = 0;
		if(!ckeepalive){
			if(param->hostname)free(param->hostname);
			param->hostname = strdup(sb);
			param->sins.sin_port = htons(80);
		}
		newbuf = malloc(strlen((char *)req) + strlen((char *)(buf+inbuf)) + 8);
		if(newbuf){
			sp = (unsigned char *)strchr((char *)req+1, '/');
			memcpy(newbuf, req, (sp - req));
			sprintf((char*)newbuf + (sp - req), "http://%s%s",sb,sp);
			free(req);
			req = newbuf;
		}
		if(se)*se = '\r';
	}
	if(i> 15 && (!strncasecmp((char *)(buf+inbuf), "content-length", 14))){
		sb = (unsigned char *)strchr((char *)(buf+inbuf), ':');
		if(!sb)continue;
		++sb;
		while(isspace(*sb))sb++;
		contentlength = atoi((char *)sb);
		if(contentlength < 0)contentlength = 0;
	}
	inbuf += i;
	if((bufsize - inbuf) < LINESIZE){
		if (bufsize > 20000){
			RETURN (516);
		}
		if(!(newbuf = realloc(buf, bufsize + BUFSIZE))){RETURN (21);}
		buf = newbuf;
		bufsize += BUFSIZE;
	}
 } while( (i = sockgetline(param->clisock, buf + inbuf, LINESIZE - 2, '\n', timeouts[STRING_S])) > 2);

 buf[inbuf] = 0;

 if((res = (*param->authfunc)(param))) {RETURN(res);}
 if (param->sins.sin_addr.s_addr == param->intip && param->sins.sin_port == param->intport) {
	RETURN(519);
 }
 if(ftp){
	SOCKET s;
	int mode = 0;
	int i=0;
	unsigned char ftpbuf[1024];

	if(!ckeepalive){
		res = ftplogin(param);
		if(res)RETURN(res);
	}
	ckeepalive = 1;
	if(ftpbase) free(ftpbase);
	ftpbase = NULL;
	if(!(sp = strchr(ss, ' '))){RETURN(799);}
	*sp = 0;

	decodeurl(ss);
	res = ftpcd(param, ss);
	if(res){
		res = ftptype(param, "I");
		if(res)RETURN(res);
		ftps = ftpcommand(param, "RETR", ss);
	}
	else {
		if(ss[strlen(ss) - 1] != '/'){
			sprintf(ftpbuf, "%.500s/", ss);
			ftpbase = strdup(ftpbuf);
		}
		else {
		}
		ftps = ftpcommand(param, "LIST", NULL);
		mode = 1;
	}
	if(ftps == INVALID_SOCKET){RETURN(780);}
	if(!mode){
		send(param->clisock, proxy_stringtable[8], strlen(proxy_stringtable[8]), 0);
		s = param->remsock;
		param->remsock = ftps;
		res = sockmap(param, timeouts[CONNECTION_L]);
		close(ftps);
		param->remsock = s;
	}
	else {
		int headsent = 0;

		memcpy(buf, "<pre>\n", 6);
		inbuf = 6;
		for(; (res = sockgetline(ftps, ftpbuf, 1023, '\n', timeouts[STRING_S])) > 0; i++){
			int isdir = 0;
			int islink = 0;
			int filetoken =-1;
			int sizetoken =-1;
			int modetoken =-1;
			int datetoken =-1;
			unsigned char * tokens[10];
			unsigned wordlen [10];
			unsigned char j=0;
			int space = 1;

			param->statssrv += res;
			ftpbuf[res] = 0;
			if(!i && ftpbuf[0] == 't' && ftpbuf[1] == 'o' && ftpbuf[2] == 't'){
				mode = 2;
				continue;
			}
			if(!isnumber(*ftpbuf) && mode == 1) mode = 2;
			for(sb=ftpbuf; *sb; sb++){
				if(!space && isspace(*sb)){
					space = 1;
					wordlen[j]=sb-tokens[j];
					j++;
				}
				if(space && !isspace(*sb)){
					space = 0;
					tokens[j] = sb;
					if(j==10)break;
				}				
			}
			if(mode == 1){
				if(j < 4) continue;
				if(!(isdir = !memcmp(tokens[2], "<DIR>", wordlen[2])) && !isnumber(*tokens[2])){
					continue;
				}
				datetoken = 0;
				wordlen[datetoken] = tokens[1] - tokens[0] + wordlen[1];
				sizetoken = 2;
				filetoken = 3;
			}
			else {
				if(j < 8 || wordlen[0]!=10) continue;
				if(j < 8 || !isnumber(*tokens[4])) mode = 3;
				if(*tokens[0] == 'd') isdir = 1;
				if(*tokens[0] == 'l') islink = 1;
				modetoken = 0;
				sizetoken = (mode == 2)? 4:3;
				filetoken = (mode == 2)? 8:7;
				datetoken = (mode == 2)? 5:4;
				wordlen[datetoken] = tokens[datetoken+3] - tokens[datetoken];
			}

			if(modetoken >= 0) memcpy(buf+inbuf, tokens[modetoken], 11);
			else memcpy(buf+inbuf, "---------- ", 11);
			inbuf += 11;
			if(wordlen[datetoken]+256 > bufsize-inbuf) continue;
			memcpy(buf+inbuf, tokens[datetoken], wordlen[datetoken]);
			inbuf += wordlen[datetoken];
			if(isdir){
				memcpy(buf+inbuf, "       DIR", 10);
				inbuf+=10;
			}
			else if(islink){
				memcpy(buf+inbuf, "      LINK", 10);
				inbuf+=10;
			}
			else{
				unsigned k;
				if(wordlen[sizetoken]>10) wordlen[sizetoken] = 10;
				for(k=10; k > wordlen[sizetoken]; k--){
					buf[inbuf++] = ' ';
				}
				memcpy(buf+inbuf, tokens[sizetoken], wordlen[sizetoken]);
				inbuf+=wordlen[sizetoken];
			}
			memcpy(buf+inbuf, " <A HREF=\"", 10);
			inbuf+=10;
			sb = NULL;
			if(islink) sb = strstr(tokens[filetoken], " -> ");
			if(sb) sb+=4;

			else sb=tokens[filetoken]; 
			if(ftpbase)file2url(ftpbase, buf, bufsize, &inbuf);
			file2url(sb, buf, bufsize, &inbuf);

			if(isdir)buf[inbuf++] = '/';
			memcpy(buf+inbuf, "\">", 2);
			inbuf+=2;
			for(sb=tokens[filetoken]; *sb; sb++){
				if((bufsize - inbuf)<16)break;
				if(*sb == '<'){
					memcpy(buf+inbuf, "&lt;", 4);
					inbuf+=4;
				}
				else if(*sb == '>'){
					memcpy(buf+inbuf, "&gt;", 4);
					inbuf+=4;
				}
				else if(*sb == '\r' || *sb=='\n'){
					continue;
				}
				else if(islink && sb[0] == ' ' && sb[1] == '-' 
				 && sb[2] == '>'){
					memcpy(buf+inbuf, "</A> ", 5);
					inbuf+=5;
				}
				else buf[inbuf++]=*sb;
			}
			if(islink!=2)memcpy(buf+inbuf, "</A>", 4);
			inbuf+=4;
			buf[inbuf++] = '\n';

			if((bufsize - inbuf) < LINESIZE){
				if (bufsize > 20000){
					if(!headsent++){
						send(param->clisock, proxy_stringtable[9], strlen(proxy_stringtable[9]), 0);
					}
					if((unsigned)send(param->clisock, buf, inbuf, 0) != inbuf){
						RETURN(781);
					}
					inbuf = 0;
				}
				else {
					if(!(newbuf = realloc(buf, bufsize + BUFSIZE))){RETURN (21);}
					buf = newbuf;
					bufsize += BUFSIZE;
				}
			}
		}
		close(ftps);
		if(inbuf){
			if(!headsent){
				sprintf(ftpbuf, 
					"HTTP/1.0 200 OK\r\n"
					"Content-Type: text/html\r\n"
					"Proxy-Connection: keep-alive\r\n"
					"Content-Length: %d\r\n\r\n",
					inbuf);
				send(param->clisock, ftpbuf, strlen(ftpbuf), 0);
			}
			send(param->clisock, buf, inbuf, 0);
			res = ftpres(param);
			if(res){RETURN(res);}
			if(!headsent)goto REQUESTEND;
		}
		RETURN(0);
	}
	RETURN(res);
 }

 if(isconnect) {
	send(param->clisock, proxy_stringtable[8], strlen(proxy_stringtable[8]), 0);
	RETURN(sockmap(param, timeouts[CONNECTION_L]));
 }

 if(!req || param->redirtype != R_HTTP) {
	reqlen = 0;
 }

 else {
	 redirect = 1;
	 if(send(param->remsock, req , (res = strlen((char *)req)), 0) != res) {RETURN(518);}
	 param->statscli += res;
 }
 inbuf = 0;
#ifndef ANONYMOUS
		sprintf((char*)buf+strlen((char *)buf), "Via: 1.1 ");
		gethostname((char *)(buf+strlen((char *)buf)), 256);
		sprintf((char*)buf+strlen((char *)buf), ":%d (%s %s)\r\nX-Forwarded-For: ", (int)ntohs(param->intport), stringtable?stringtable[2]:(unsigned char *)"", stringtable?stringtable[3]:(unsigned char *)"");
		myinet_ntoa(param->sinc.sin_addr, buf + strlen(buf));
		sprintf((char*)buf+strlen((char *)buf), "\r\n");
#endif
 if(keepalive)sprintf((char*)buf+strlen((char *)buf), "%s: Keep-Alive\r\n", (redirect)?"Proxy-Connection":"Connection");
 if(param->extusername){
	sprintf((char*)buf + strlen((char *)buf), "%s: basic ", (redirect)?"Proxy-Authorization":"Authorization");
	sprintf((char*)username, "%.32s:%.64s", param->extusername, param->extpassword?param->extpassword:(unsigned char*)"");
	en64(username, buf+strlen((char *)buf), strlen((char *)username));
	sprintf((char*)buf + strlen((char *)buf), "\r\n");
 }
 sprintf((char*)buf+strlen((char *)buf), "\r\n");
 if ((res = send(param->remsock, buf+reqlen, strlen((char *)buf+reqlen), 0)) != (int)strlen((char *)buf+reqlen)) {
	RETURN(518);
 }
 param->statscli += res;
/*
 if(!keepalive || isconnect){
	RETURN (sockmap(param, timeouts[CONNECTION_S]));
 }
*/
 if(contentlength > 0){
	 param->waitclient = contentlength;
	 res = sockmap(param, timeouts[CONNECTION_S]);
	 param->waitclient = 0;
	 if(res != 99) {
		RETURN(res);
	}
 }
 contentlength = -1;
 inbuf = 0;
 ckeepalive = 1;
 res = 0; 
 while( (i = sockgetline(param->remsock, buf + inbuf, LINESIZE - 1, '\n', timeouts[(res)?STRING_S:STRING_L])) > 2) {
	if(!res && i>9)res = *(buf + inbuf + 9);
	if(((i> 25 && redirect && !strncasecmp((char *)(buf+inbuf), "proxy-connection:", 17))
	   ||
	    (i> 16 && !redirect && !strncasecmp((char *)(buf+inbuf), "connection:", 11))
			)){
		sb = (unsigned char *)strchr((char *)(buf+inbuf), ':');
		if(!sb)continue;
		++sb;
		while(isspace(*sb))sb++;
		if(!strncasecmp((char *)sb,"keep-alive", 10))ckeepalive = 1;
		else ckeepalive = 0;
		continue; 
	}
	if(i> 6 && (!strncasecmp((char *)(buf+inbuf), "proxy-", 6))){
		continue; 
	}
	if(i > 15 && (!strncasecmp((char *)(buf+inbuf), "content-length", 14))){
		buf[inbuf+i]=0;
		sb = (unsigned char *)strchr((char *)(buf+inbuf), ':');
		if(!sb)continue;
		++sb;
		while(isspace(*sb))sb++;
		contentlength = atoi((char *)sb);
		if(contentlength < 0)contentlength = 0;
	}
	inbuf += i;
	if((bufsize - inbuf) < LINESIZE){
		if (bufsize > 20000){
			RETURN (516);
		}
		if(!(newbuf = realloc(buf, bufsize + BUFSIZE))){RETURN (21);}
		buf = newbuf;
		bufsize += BUFSIZE;
	}
 }
 if(res != '2' && res != '3') {
	ckeepalive = 0;
 }
 param->statssrv += inbuf;
 buf[inbuf] = 0;
 if(inbuf < 9) {RETURN (520);}
 sprintf((char*)buf+strlen((char *)buf), "%s: %s\r\n", 
	(transparent)?"Connection":"Proxy-Connection",
	(contentlength >= 0 && ckeepalive)?"Keep-Alive":"Close"
 );
 sprintf((char*)buf + strlen((char *)buf), "\r\n");
 if(send(param->clisock, buf, strlen((char *)buf), 0) != (int)strlen((char *)buf)) {RETURN(518);}
 if(contentlength > 0) {
	 param->waitserver = contentlength;
	 res = sockmap(param, timeouts[CONNECTION_S]);
	 param->waitserver = 0;
	 if(res != 98) {
		RETURN(res);
	 }
 }
 else if(contentlength < 0) {
	RETURN(sockmap(param, timeouts[CONNECTION_S]));
 }
 contentlength = -1;

REQUESTEND:

 if((!ckeepalive || !keepalive) && param->remsock != INVALID_SOCKET){
	shutdown(param->remsock, SHUT_RDWR);
	close(param->remsock);
	param->remsock = INVALID_SOCKET;
	RETURN(0);
 }
 if(req) {
	 sb = (unsigned char *)strchr((char *)req, '\r');
	 if(sb)*sb = 0;
 }
 (*param->logfunc)(param, (req)?req:NULL);
 param->statscli = 0;
 param->statssrv = 0;
}

CLEANRET:

 if(ftpbase) free(ftpbase);
 if(param->res != 555 && param->res && param->clisock != INVALID_SOCKET && (param->res < 90 || param->res >=800 || param->res == 780)) {
	if((param->res>=510 && param->res <=517)) while( (i = sockgetline(param->clisock, buf, BUFSIZE - 1, '\n', timeouts[STRING_S])) > 2);
	if(param->res == 10) {
		send(param->clisock, proxy_stringtable[2], strlen(proxy_stringtable[2]), 0);
	}
	else if(param->res == 100 || (param->res >10 && param->res < 20)) {
		send(param->clisock, proxy_stringtable[1], strlen(proxy_stringtable[1]), 0);
	}
	else if(param->res >=20 && param->res < 30) {
		send(param->clisock, proxy_stringtable[6], strlen(proxy_stringtable[6]), 0);
	}
	else if(param->res >=30 && param->res < 80) {
		send(param->clisock, proxy_stringtable[5], strlen(proxy_stringtable[5]), 0);
	}
	else if(param->res < 10) {
		send(param->clisock, proxy_stringtable[7], strlen(proxy_stringtable[7]), 0);
	}
	else if(param->res == 999) {
		send(param->clisock, proxy_stringtable[4], strlen(proxy_stringtable[4]), 0);
	}
	else if(param->res == 519) {
		send(param->clisock, proxy_stringtable[3], strlen(proxy_stringtable[3]), 0);
	}
	else if(param->res == 780) {
		send(param->clisock, proxy_stringtable[10], strlen(proxy_stringtable[10]), 0);
	}
	else {
		send(param->clisock, proxy_stringtable[0], strlen(proxy_stringtable[0]), 0);
	}
 } 
 if(req) {
	 sb = (unsigned char *)strchr((char *)req, '\r');
	 if(sb)*sb = 0;
 }
 if(param->res != 555)(*param->logfunc)(param, req);
 if(req)free(req);
 if(buf)free(buf);
 freeparam(param);
 return (NULL);
}

#ifdef WITHMAIN
#include "proxymain.c"
#endif
