#include <all_far.h>

#include "Int.h"

#if !defined(IPPORT_FTP)
#define IPPORT_FTP 21
#endif

/*
 * Set transfer type.
 */
BOOL Connection::settype(ftTypes Mode, LPCSTR Arg)
{
	PROC(("settype", "[%c,%s]", Mode, Arg));
	int comret;

	if (Arg)
		comret = command("TYPE %c %s", Mode, Arg);
	else
		comret = command("TYPE %c", Mode);

	if (comret == RPL_COMPLETE) {
		type = Mode;
		Log(("TYPE = %c", Mode));
		return TRUE;
	} else
		return FALSE;
}

BOOL Connection::setascii()
{
	return settype(TYPE_A, NULL);
}
BOOL Connection::setbinary()
{
	return settype(TYPE_I, NULL);
}
BOOL Connection::setebcdic()
{
	return settype(TYPE_E, NULL);
}

/*
 * Connect to peer server and auto-login, if possible.
 * 1      2       3       4
 * <site> [<port> [<user> [<pwd>]]]
 */
int Connection::setpeer(int argc, char *argv[])
{
	PROC(("setpeer", "%d [%s,%s,%s,%s]", argc, (argc > 1) ? argv[1] : "nil", (argc > 2) ? argv[2] : "nil",
			(argc > 3) ? argv[3] : "nil", (argc > 4) ? argv[4] : "nil"));
	FP_Screen _scr;
	WORD port;

	if (argc < 2) {
		code = -1;
		return FALSE;
	}

	// port
	if (argc > 2)
		port = htons(atoi(argv[2]));
	else
		port = 0;

	if (!port) {
		servent *sp = getservbyname("ftp", "tcp");

		if (!sp) {
			Log(("ftp: ftp/tcp: unknown service. Assume port number %d (default)", IPPORT_FTP));
			port = htons(IPPORT_FTP);
		} else {
			port = sp->s_port;
			Log(("ftp: port %08X", port));
		}
	}

	// Check if need to close connection
	if (connected) {
		if (StrCmpI(argv[1], hostname) != 0 || port != portnum)
			disconnect();
	}

	do {
		// Make connection
		if (!connected) {
			if (!hookup(argv[1], port))		// Open connection
			{
				code = -1;
				break;
			}

			connected = 1;
		}

		// Login
		UserName[0] = 0;
		UserPassword[0] = 0;

		if (argc > 3)
			StrCpy(UserName, argv[3], ARRAYSIZE(UserName));

		if (argc > 4)
			StrCpy(UserPassword, argv[4], ARRAYSIZE(UserPassword));

		if (!login()) {
			disconnect();
			break;
		}

		return TRUE;
	} while (0);

	return FALSE;
}

/*
 * Send a single file.
 */
void Connection::put(int argc, char *argv[])
{
	PROC(("put", "%d [\"%s\",\"%s\",\"%s\"]", argc, (argc >= 1) ? argv[0] : "nil",
			(argc >= 2) ? argv[1] : "nil", (argc >= 3) ? argv[2] : "nil"));
	char *cmd;

	if (argc == 2) {
		argc++;
		argv[2] = argv[1];
	}

	if (argc < 3) {
		code = -1;
		return;
	}

	cmd = (argv[0][0] == 'a')
			? Opt.cmdAppe									// APPEND
			: ((sunique) ? Opt.cmdPutUniq : Opt.cmdStor);	// STORE or PUT
	sendrequest(cmd, argv[1], argv[2]);
	restart_point = 0;
}

void Connection::reget(int argc, char *argv[])
{
	PROC(("reget", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));
	getit(argc, argv, 1, "a+");
}

void Connection::get(int argc, char *argv[])
{
	PROC(("get", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));
	getit(argc, argv, 0, restart_point ? "r+" : "w");
}

/*
 * get file if modtime is more recent than current file
 */
void Connection::newer(int argc, char *argv[])
{
	PROC(("newer", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));
	getit(argc, argv, -1, "w");
}

/*
 * Receive one file.
 */
int Connection::getit(int argc, char *argv[], int restartit, const char *mode)
{
	PROC(("getit", " %d %s %d [%s,%s,%s]", restartit, mode, argc, (argc >= 1) ? argv[0] : "nil",
			(argc >= 2) ? argv[1] : "nil", (argc >= 3) ? argv[2] : "nil"));

	if (argc == 2) {
		argc++;
		argv[2] = argv[1];
	}

	if (argc < 3) {
		code = -1;
		return (0);
	}

	if (restartit == -1)
		restart_point = -1;
	else if (restartit) {
		restart_point = Fsize(argv[2]);
		Log(("Restart from %I64u", restart_point));
	}

	recvrequest(Opt.cmdRetr, argv[2], argv[1], mode);
	restart_point = 0;
	return 0;
}

/*
 * Toggle PORT cmd use before each data connection.
 */
void Connection::setport()
{
	sendport = !sendport;
	code = sendport;
}

/*
 * Set current working directory
 * on remote machine.
 */
void Connection::cd(int argc, char *argv[])
{
	PROC(("cd", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));

	if (argc == 2)
		if (command("%s %s", Opt.cmdCwd, argv[1]) >= RPL_COMPLETE && code == 500)
			command("%s %s", Opt.cmdXCwd, argv[1]);
}

/*
 * Delete a single file.
 */
void Connection::deleteFile(int argc, char *argv[])
{
	PROC(("deleteFile", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));

	if (argc < 2) {
		code = -1;
		return;
	}

	command("%s %s", Opt.cmdDel, argv[1]);
}

/*
 * Rename a remote file.
 */
void Connection::renamefile(int argc, char *argv[])
{
	PROC(("renamefile", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));

	if (argc < 3) {
		code = -1;
		return;
	}

	if (command("%s %s", Opt.cmdRen, argv[1]) == RPL_CONTINUE)
		command("%s %s", Opt.cmdRenTo, argv[2]);
}

/*
 * Get a directory listing
 * of remote files.
 */
void Connection::ls(int argc, char *argv[])
{
	PROC(("ls", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));

	if (argc < 2)
		argc++, argv[1] = NULL;

	if (argc < 3)
		argc++, argv[2] = (char *)"-";

	if (argc > 3) {
		code = -1;
		return;
	}

	if (argv[0][0] == 'n')
		recvrequest(Opt.cmdNList, argv[2], argv[1], "w");
	else if (!Host.ExtList)
		recvrequest(Opt.cmdList, argv[2], argv[1], "w");
	else {
		recvrequest(Host.ListCMD, argv[2], argv[1], "w");

		if (code > 500 && argv[1] != NULL)
			recvrequest(Opt.cmdList, argv[2], argv[1], "w");
	}
}

/*
 * Send new user information (re-login)
 */
int Connection::user(int argc, char **argv)
{
	PROC(("user", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));
	char acct[80];
	int n, aflag = 0;

	if (argc < 2 || argc > 4) {
		code = -1;
		return (0);
	}

	n = command("%s %s", Opt.cmdUser, argv[1]);

	if (n == RPL_CONTINUE) {
		if (argc < 3)
			argv[2] = UserPassword, argc++;

		n = command("%s %s", Opt.cmdPass, argv[2]);
	}

	if (n == RPL_CONTINUE) {
		if (argc < 4) {
			*acct = 0;
			argv[3] = acct;
			argc++;
		}

		n = command("%s %s", Opt.cmdAcct, argv[3]);
		aflag++;
	}

	if (n != RPL_COMPLETE) {
		WINPORT(SetLastError)(EPERM);	// ERROR_INTERNET_LOGIN_FAILURE);
		return 0;
	}

	if (!aflag && argc == 4)
		command("%s %s", Opt.cmdAcct, argv[3]);

	return 1;
}

/*
 * Print working directory.
 */
void Connection::pwd()
{
	PROC(("pwd", NULL));

	if (command(Opt.cmdPwd) >= RPL_COMPLETE && code == 500) {
		Log(("Try XPWD [%s]", Opt.cmdXPwd));
		command(Opt.cmdXPwd);
	}
}

/*
 * Make a directory.
 */
void Connection::makedir(int argc, char *argv[])
{
	PROC(("makedir", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));

	if (argc < 2) {
		code = -1;
		return;
	}

	if (command("%s %s", Opt.cmdMkd, argv[1]) >= RPL_COMPLETE && code == 500)
		command("%s %s", Opt.cmdXMkd, argv[1]);
}

/*
 * Remove a directory.
 */
void Connection::removedir(int argc, char *argv[])
{
	PROC(("removedir", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));

	if (argc < 2) {
		code = -1;
		return;
	}

	if (command("%s %s", Opt.cmdRmd, argv[1]) >= RPL_COMPLETE && code == 500)
		command("%s %s", Opt.cmdXRmd, argv[1]);
}

/*
 * Send a line, verbatim, to the remote machine.
 */
void Connection::quote(int argc, char *argv[])
{
	PROC(("quote", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));
	int i;
	char buf[BUFSIZ];

	if (argc < 2) {
		code = -1;
		return;
	}

	strcpy(buf, argv[1]);

	for (i = 2; i < argc; i++) {
		strcat(buf, " ");
		strcat(buf, argv[i]);
	}

	if (command(buf) == RPL_PRELIM)
		while (getreply(0) == RPL_PRELIM)
			;
}

/*
 * Send a SITE command to the remote machine.  The line
 * is sent almost verbatim to the remote machine, the
 * first argument is changed to SITE.
 */

void Connection::site(int argc, char *argv[])
{
	PROC(("site", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));
	int i;
	char buf[BUFSIZ];

	if (argc < 2) {
		code = -1;
		return;
	}

	strcpy(buf, Opt.cmdSite);
	strcat(buf, " ");
	strcat(buf, argv[1]);

	for (i = 2; i < argc; i++) {
		strcat(buf, " ");
		strcat(buf, argv[i]);
	}

	if (command(buf) == RPL_PRELIM)
		while (getreply(0) == RPL_PRELIM)
			;
}

void Connection::do_chmod(int argc, char *argv[])
{
	PROC(("do_chmod", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));

	if (argc != 3) {
		code = -1;
		return;
	}

	command("%s %s %s %s", Opt.cmdSite, Opt.cmdChmod, argv[1], argv[2]);
}

void Connection::do_umask(int argc, char *argv[])
{
	PROC(("do_umask", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));
	command(argc == 1 ? "%s %s" : "%s %s %s", Opt.cmdSite, Opt.cmdUmask, argv[1]);
}

void Connection::idle(int argc, char *argv[])
{
	PROC(("idle", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));
	command(argc == 1 ? "%s %s" : "%s %s %s", Opt.cmdSite, Opt.cmdIdle, argv[1]);
}

/*
 * Terminate session and exit.
 */
/*VARARGS*/
void Connection::quit()
{
	PROC(("quit", NULL));

	if (connected)
		disconnect();

	pswitch(1);

	if (connected)
		disconnect();
}

/*
 * Terminate session, but don't exit.
 */
void Connection::disconnect()
{
	PROC(("disconnect", NULL));

	if (!connected)
		return;

	command(Opt.cmdQuit);
	cout = 0;
	connected = 0;
	scClose(data_peer, -1);
}

void Connection::account(int argc, char **argv)
{
	PROC(("account", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));
	String acct;
	const char *ap;

	if (argc > 1) {
		++argv;
		--argc;
		acct = *argv;

		while (argc > 1) {
			--argc;
			++argv;
			acct.cat(*argv);
		}

		ap = acct.c_str();
	} else
		ap = "";	//    ap = getpass("Account:");

	command("%s %s", Opt.cmdAcct, ap);
}

void Connection::doproxy(int argc, char *argv[])
{
	PROC(("doproxy", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));
	struct cmd *c;

	if (argc < 2) {
		code = -1;
		return;
	}

	c = getcmd(argv[1]);

	// Not found
	if (c == (cmd *)-1 || c == 0) {
		WINPORT(SetLastError)(ERROR_BAD_DRIVER_LEVEL);
		code = -1;
		return;
	}

	// Unsupported in proxy mode
	if (!c->c_proxy) {
		WINPORT(SetLastError)(ERROR_BAD_NET_RESP);
		code = -1;
		return;
	}

	// Already in proxy mode
	if (proxy > 0)
		ExecCmdTab(c, argc - 1, argv + 1);
	else {
		// Temp switch to proxy mode
		pswitch(1);

		if (c->c_conn && !connected) {
			pswitch(0);
			code = -1;
			return;
		}

		ExecCmdTab(c, argc - 1, argv + 1);
		proxflag = (connected) ? 1 : 0;
		pswitch(0);
	}
}

void Connection::setsunique()
{
	sunique = !sunique;
	code = sunique;
}

void Connection::setrunique()
{
	runique = !runique;
	code = runique;
}

/* change directory to parent directory */
void Connection::cdup()
{
	PROC(("cdup", NULL));

	if (command(Opt.cmdCDUp) >= RPL_COMPLETE && code == 500)
		command(Opt.cmdXCDUp);
}

/* restart transfer at specific point */
void Connection::restart(int argc, char *argv[])
{
	PROC(("restart", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));

	if (argc == 2)
		restart_point = atol(argv[1]);
}

/* show remote system type */
void Connection::syst()
{
	PROC(("syst", NULL));
	command(Opt.cmdSyst);
}

/*
 * get size of file on remote machine
 */
void Connection::sizecmd(int argc, char *argv[])
{
	PROC(("sizecmd", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));

	if (argc < 2) {
		code = -1;
		return;
	}

	command("%s %s", Opt.cmdSize, argv[1]);
}

/*
 * get last modification time of file on remote machine
 */
void Connection::modtime(int argc, char *argv[])
{
	PROC(("modtime", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));

	if (argc < 2) {
		code = -1;
		return;
	}

	if (command("%s %s", Opt.cmdMDTM, argv[1]) == RPL_COMPLETE) {
		int yy, mo, day, hour, min, sec;
		sscanf(reply_string.c_str(), Opt.fmtDateFormat, &yy, &mo, &day, &hour, &min, &sec);
	}
}

/*
 * show status on remote machine
 */
void Connection::rmtstatus(int argc, char *argv[])
{
	PROC(("rmtstatus", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));
	command(argc > 1 ? "%s %s" : "%s", Opt.cmdStat, argv[1]);
}

/*
 * Ask the other side for help.
 */
void Connection::rmthelp(int argc, char *argv[])
{
	PROC(("rmthelp", "%d [%s,%s,%s]", argc, (argc >= 1) ? argv[0] : "nil", (argc >= 2) ? argv[1] : "nil",
			(argc >= 3) ? argv[2] : "nil"));
	command(argc == 1 ? "%s" : "%s %s", Opt.cmdHelp, argv[1]);
}
