#include <all_far.h>


#include "Int.h"

//--------------------------------------------------------------------------------
BOOL Connection::SetType(int type)
{
	switch(type)
	{
		case TYPE_A:
			return setascii();
		case TYPE_I:
			return setbinary();
		case TYPE_E:
			return setebcdic();
	}

	return FALSE;
}

void Connection::reset()
{
	fd_set mask;
	int           nfnd;

	do
	{
		FD_ZERO(&mask);
		FD_SET(cin, &mask);  /*!*/
		nfnd = empty(&mask,0);

		if(nfnd < 0)
		{
			Log(("!reset"));
			code = -1;
			lostpeer();
		}
		else if(nfnd)
			getreply(0);
	}
	while(nfnd > 0);
}

void Connection::AbortAllRequest(int BrkFlag)
{
	if(BrkFlag)
		brk_flag = TRUE;
	else
	{
		scClose(data_peer,-1);
		scClose(cmd_peer,-1);
	}
}

int Connection::empty(fd_set *mask, int sec)
{
	struct timeval t;
	t.tv_sec = (long) sec;
	t.tv_usec = 0;
	return select(FD_SETSIZE,mask,NULL,NULL,&t);
}

//--------------------------------------------------------------------------------
/*VARARGS1*/
int Connection::command(const char *fmt, ...)
{
	PROC(("Connection::command", "%s", fmt));
	va_list ap;
	int     r;
	String  buffer;

	if(!cout)
	{
		Log(("!control connection for command [%s]",fmt));
		code = -1;
		return (0);
	}

	va_start(ap, fmt);
	buffer.vprintf(fmt, ap);
	va_end(ap);
	buffer.cat("\r\n");

	if(!fputsSocket(buffer.c_str(), cout))
	{
		Log(("!puts"));
		lostpeer();
		code = -1;
		return RPL_ERROR;
	}

	cpend = 1;
	r = getreply(StrCmp(fmt,Opt.cmdQuit) == 0);
	Log(("reply rc=%d",r));
#if 0/*?*/

	while(r != RPL_ERROR &&
	        !brk_flag &&
	        ((StrCmp(fmt,Opt.cmdPwd)==0 && (reply_string.Chr('\"') == -1) ||
	          !isdigit(reply_string[0]) ||
	          !isdigit(reply_string[1]) ||
	          !isdigit(reply_string[2]) ||
	          !isspace(reply_string[3]))))
	{
		r = getreply(FALSE);
	}

#endif
	Log(("rc=%d",r));
	return r;
}

//--------------------------------------------------------------------------------
int Connection::getreply(BOOL expecteof, DWORD tm)
{
	int   c, n, dig;
	int   originalcode = 0,
	      continuation = 0,
	      pflag        = 0;
	char *pt = pasv;

	if(cin == INVALID_SOCKET)
	{
		Log(("gr: inv socket"));
		code = 421;
		return RPL_ERROR;
	}

	for(;;)
	{
		dig = n = code = 0;
		reply_string = "";

		//Read line
		while((c=fgetcSocket(cin,tm)) != '\n')
		{
			if(c == RPL_TIMEOUT)
			{
				return RPL_TIMEOUT;
			}

			if(!c)
			{
				Log(("gr conn closed"));
				lostpeer();
				code = 421;
				return RPL_ERROR;
			}

			if(!dig)
			{
				// handle telnet commands
				if(c == ffIAC)
				{
					switch(c = fgetcSocket(cin))
					{
						case ffWILL:
						case ffWONT:
							c = fgetcSocket(cin);
							fprintfSocket(cout, "%c%c%c",ffIAC,ffDONT,c);
							break;
						case ffDO:
						case ffDONT:
							c = fgetcSocket(cin);
							fprintfSocket(cout, "%c%c%c",ffIAC,ffWONT,c);
							break;
						default:
							break;
					}

					continue;
				}

				if(!c || c == ffEOF)
				{
					Log(("gr EOF: c: %d (%d)",c,expecteof));

					if(expecteof)
					{
						code = 221;
						return RPL_OK;
					}

					lostpeer();
					code = 421;
					return RPL_TRANSIENT;
				}
			}

			dig++;

			if(dig < 4 && isdigit(c))
				code = code * 10 + (c - '0');

			if(!pflag && code == 227)
				pflag = 1;

			if(dig > 4 && pflag == 1 && isdigit(c))
				pflag = 2;

			if(pflag == 2)
			{
				if(c != '\r' && c != ')')
					*pt++ = c;
				else
				{
					*pt = '\0';
					pflag = 3;
				}
			}

			if(dig == 4 && c == '-')
			{
				if(continuation)
					code = 0;

				continuation++;
			}

			if(n == 0)
				n = c;

			reply_string.Add((char)c);
		}

		AddCmdLine(NULL);

		if(continuation && code != originalcode)
		{
			//Log(( "Continue: Cont: %d Code: %d Orig: %d", continuation,code,originalcode ));
			if(originalcode == 0)
				originalcode = code;

			continue;
		}

		if(n != '1')
			cpend = 0;

		if(code == 421 || originalcode == 421)
		{
			//Log(( "Code=%d, Orig=%d -> lostpeer",code,originalcode ));
			lostpeer();
		}

		//Log(( "rc = %d", (n - '0') ));
		return (n - '0');
	}
}

//--------------------------------------------------------------------------------
void Connection::pswitch(int flag)
{
	struct comvars *ip, *op;

	if(flag)
	{
		if(proxy) return;

		ip = &tmpstruct;
		op = &proxstruct;
		proxy++;
	}
	else
	{
		if(!proxy) return;

		ip = &proxstruct;
		op = &tmpstruct;
		proxy = 0;
	}

	ip->connect = connected;
	connected = op->connect;

	if(hostname)
	{
		StrCpy(ip->name, hostname, ARRAYSIZE(ip->name) - 1);
		ip->name[strlen(ip->name)] = '\0';
	}
	else
		ip->name[0] = 0;

	StrCpy(hostname,op->name,ARRAYSIZE(hostname));
	ip->hctl = hisctladdr;
	hisctladdr = op->hctl;
	ip->mctl = myctladdr;
	myctladdr = op->mctl;
	ip->in = cin; // What the hell am I looking at...?
	cin = op->in;
	ip->out = cout; // Same again...
	cout = op->out;
	ip->tpe = type;
	type = op->tpe;

	if(!type)
		type = TYPE_A;

	ip->cpnd = cpend;
	cpend = op->cpnd;
	ip->sunqe = sunique;
	sunique = op->sunqe;
	ip->runqe = runique;
	runique = op->runqe;
}

//--------------------------------------------------------------------------------
void Connection::proxtrans(char *cmd, char *local, char *remote)
{
	int tmptype, oldtype = 0, secndflag = 0, nfnd;
	char *cmd2;
	fd_set mask;

	if(StrCmp(cmd,Opt.cmdRetr) != 0)
		cmd2 = Opt.cmdRetr;
	else
		cmd2 = runique ? Opt.cmdPutUniq : Opt.cmdStor;

	if(command(Opt.cmdPasv) != RPL_COMPLETE)
	{
		ConnectMessage(MProxyNoThird,NULL,-MOk);
		return;
	}

	tmptype = type;
	pswitch(0);

	if(!connected)
	{
		ConnectMessage(MNotConnected,NULL,-MOk);
		pswitch(1);
		code = -1;
		return;
	}

	if(type != tmptype)
	{
		oldtype = type;
		SetType(tmptype);
	}

	if(command("%s %s",Opt.cmdPort,pasv) != RPL_COMPLETE)
	{
		SetType(oldtype);
		pswitch(1);
		return;
	}

	if(command("%s %s", cmd, remote) != RPL_PRELIM)
	{
		SetType(oldtype);
		pswitch(1);
		return;
	}

	pswitch(1);
	secndflag++;

	if(command("%s %s", cmd2, local) != RPL_PRELIM)
		goto abort;

	getreply(0);
	pswitch(0);
	getreply(0);
	SetType(oldtype);
	pswitch(1);
	return;
abort:

	if(StrCmp(cmd,Opt.cmdRetr) != 0 && !proxy)
		pswitch(1);
	else if(StrCmp(cmd,Opt.cmdRetr) == 0 && proxy)
		pswitch(0);

	if(!cpend && !secndflag)     /* only here if cmd = "STOR" (proxy=1) */
	{
		if(command("%s %s", cmd2, local) != RPL_PRELIM)
		{
			pswitch(0);
			SetType(oldtype);

			if(cpend)
			{
				char msg[2];
				fprintfSocket(cout,"%c%c",ffIAC,ffIP);
				*msg = ffIAC;
				*(msg+1) = ffDM;

				if(nb_send(&cout,msg,2,MSG_OOB) != RPL_COMPLETE)
				{
					Log(("!send [%s:%d]",__FILE__ , __LINE__));
				}

				fprintfSocket(cout,"ABOR\r\n");
				FD_ZERO(&mask);
				FD_SET(cin, &mask); /*!*/ // Chris: Need to correct this

				if((nfnd = empty(&mask,10)) <= 0)
				{
					Log(("abort"));
					lostpeer();
				}

				if(getreply(0) != RPL_ERROR)
					getreply(0);
				else
				{
					lostpeer();
					return;
				}
			}
		}

		pswitch(1);
		return;
	}

	if(cpend)
	{
		char msg[2];
		fprintfSocket(cout,"%c%c",ffIAC,ffIP);
		*msg = ffIAC;
		*(msg+1) = ffDM;

		if(nb_send(&cout,msg,2,MSG_OOB) != RPL_COMPLETE)
		{
			Log(("!send [%s:%d]",__FILE__ , __LINE__));
		}

		fprintfSocket(cout,"ABOR\r\n");
		FD_ZERO(&mask);
		/*!*/
		FD_SET(cin, &mask); // Chris: Need to correct this...

		if((nfnd = empty(&mask,10)) <= 0)
		{
			if(nfnd < 0)
			{
				Log(("abort"));
			}

			lostpeer();
		}

		if(getreply(0) != RPL_ERROR)
			getreply(0);
		else
		{
			lostpeer();
			return;
		}
	}

	pswitch(!proxy);

	if(!cpend && !secndflag)     /* only if cmd = "RETR" (proxy=1) */
	{
		if(command("%s %s", cmd2, local) != RPL_PRELIM)
		{
			pswitch(0);
			SetType(oldtype);

			if(cpend)
			{
				char msg[2];
				fprintfSocket(cout,"%c%c",ffIAC,ffIP);
				*msg = ffIAC;
				*(msg+1) = ffDM;

				if(nb_send(&cout,msg,2,MSG_OOB) != RPL_COMPLETE)
				{
					Log(("!send [%s:%d]",__FILE__ , __LINE__));
				}

				fprintfSocket(cout,"ABOR\r\n");
				FD_ZERO(&mask);
				/*!*/
				FD_SET(cin, &mask); // Chris:

				if((nfnd = empty(&mask,10)) <= 0)
				{
					if(nfnd < 0)
					{
						Log(("abort"));
					}

					lostpeer();
				}

				if(getreply(0) != RPL_ERROR)
					getreply(0);
				else
				{
					lostpeer();
					return;
				}
			}

			pswitch(1);
			return;
		}
	}

	if(cpend)
	{
		char msg[2];
		fprintfSocket(cout,"%c%c",ffIAC,ffIP);
		*msg = ffIAC;
		*(msg+1) = ffDM;

		if(nb_send(&cout,msg,2,MSG_OOB) != RPL_COMPLETE)
		{
			Log(("!send [%s:%d]",__FILE__ , __LINE__));
		}

		fprintfSocket(cout,"ABOR\r\n");
		FD_ZERO(&mask);
		/*!*/
		FD_SET(cin, &mask);

		if((nfnd = empty(&mask,10)) <= 0)
		{
			if(nfnd < 0)
			{
				Log(("abort"));
			}

			lostpeer();
		}

		if(getreply(0) != RPL_ERROR)
			getreply(0);
		else
		{
			lostpeer();
			return;
		}
	}

	pswitch(!proxy);

	if(cpend)
	{
		FD_ZERO(&mask);
		/*!*/
		FD_SET(cin, &mask); // Chris:

		if((nfnd = empty(&mask,10)) <= 0)
		{
			if(nfnd < 0)
			{
				Log(("abort"));
			}

			lostpeer();
		}

		if(getreply(0) != RPL_ERROR)
			getreply(0);
		else
		{
			lostpeer();
			return;
		}
	}

	if(proxy)
		pswitch(0);

	SetType(oldtype);
	pswitch(1);
}
