#include <all_far.h>

#include "Int.h"
#include <KeyFileHelper.h>


#define FTPHOST_DVERSION                 (2740)
#define FTPHOST_DVERSION_SERVERTYPE      (FTPHOST_DVERSION + sizeof(WORD))
#define FTPHOST_DVERSION_SERVERTYPE_CODE (FTPHOST_DVERSION_SERVERTYPE + sizeof(BOOL))

#define FTPHOST_VERSION_LAST             FTPHOST_DVERSION_SERVERTYPE_CODE

#define FTPHOST_VERSION      Message("1.%d", FTPHOST_VERSION_LAST )

#undef PROC
#undef Log
#if 0
#define Log(v)  INProc::Say v
#define PROC(v) INProc _inp v ;
#else
#define PROC(v)
#define Log(v)
#endif

//---------------------------------------------------------------------------------
BOOL WarnOldFormat(FTPHost* h)
{
	static LPCSTR items[] =
	{
		FMSG(MOldHostFormatTitle),
		FMSG(MOldHostFormat1),
		NULL,
		FMSG("\x1"),
		FMSG(MOldHostFormat2),
		FMSG(MOldHostFormat3),
		FMSG("\x1"),
		FMSG(MOldHostConvert), FMSG(MCancel)
	};
	items[2] = h->HostName;
	return FMessage(FMSG_WARNING | FMSG_LEFTALIGN, "WarnOldFmt", items, ARRAYSIZE(items), 2) == 0;
}

//---------------------------------------------------------------------------------
typedef BOOL (*HexToPassword_cb)(char *HexStr,char *Password);

void PasswordToHex(char *Password,char *HexStr)
{
	int  n;
	BYTE pwd[FTP_PWD_LEN];
	MakeCryptPassword(Password,pwd);
	strcpy(HexStr,"hex:");

	for(n = 0,HexStr += 4; n < FTP_PWD_LEN && pwd[n]; Password++,n++)
	{
		sprintf(HexStr,"%02x",pwd[n]);
		HexStr+=2;
	}
}
BYTE HexToNum(char Hex)
{
	if(Hex >= '0' && Hex <= '9')
		return (BYTE)(Hex-'0');
	if(Hex >= 'A' && Hex <= 'Z')
		return (BYTE)(Hex-'A'+10);
	if(Hex >= 'a' && Hex <= 'z')
		return (BYTE)(Hex-'a'+10);
	return 0;	
}

BOOL HexToPassword_OLD(char *HexStr,char *Password)
{
	if(strncmp(HexStr,"hex:",4) != 0)
		return FALSE;

	HexStr+=4;

	while(*HexStr)
	{
		*Password = HexToNum(HexStr[0])*16+HexToNum(HexStr[1]);
		Password++;
		HexStr+=2;
	}

	*Password = 0;
	return TRUE;
}

BOOL HexToPassword_2740(char *HexStr,char *Password)
{
	BYTE pwd[FTP_PWD_LEN];
	int  n;

	if(strncmp(HexStr,"hex:",4) != 0)
		return FALSE;

	for(n = 0, HexStr+=4;
	        n < FTP_PWD_LEN &&
	        *HexStr;
	        n++, HexStr+=2)
		pwd[n] = HexToNum(HexStr[0])*16 + HexToNum(HexStr[1]);

	pwd[n] = 0;
	DecryptPassword(pwd,Password);
	return TRUE;
}

inline BOOL HexToPassword_CUR(char *HexStr,char *Password)
{
	return HexToPassword_2740(HexStr,Password);
}

//---------------------------------------------------------------------------------
void FTPHost::Init(void)
{
	Log(("FTPHost::Init %p",this));
	memset(this,0,sizeof(*this));
	Size         = sizeof(*this);
	ExtCmdView   = Opt.ExtCmdView;
	IOBuffSize   = Opt.IOBuffSize;
	PassiveMode  = Opt.PassiveMode;
	ProcessCmd   = Opt.ProcessCmd;
	UseFirewall  = Opt.Firewall[0] != 0;
	FFDup        = Opt.FFDup;
	UndupFF      = Opt.UndupFF;
	DecodeCmdLine= TRUE;
	ServerType   = FTP_TYPE_DETECT;
	StrCpy(ListCMD, "LIST -la", ARRAYSIZE(ListCMD));
}

void FTPHost::Assign(FTPHost* p)
{
	Assert(p);
	Assert(p != this);
	memcpy(this,p,sizeof(*this));
}

BOOL FTPHost::Cmp(FTPHost* p)
{
	return strcmp(RegKey,p->RegKey) == 0;
}

BOOL FTPHost::CmpConnected(FTPHost* p)
{
	return StrCmp(p->Host,      Host,      -1, FALSE) == 0 &&
	       StrCmp(p->User,      User,      -1, TRUE)  == 0 &&
	       StrCmp(p->Password,  Password,  -1, TRUE)  == 0;
}

void FTPHost::MkUrl(String& str,LPCSTR Path,LPCSTR nm,BOOL sPwd)
{
	bool defPara = StrCmp(User,"anonymous") == 0 &&
	               StrCmp(Password,Opt.DefaultPassword) == 0;

	if(!defPara && User[0])
	{
		if(Password[0])
			str.printf("ftp://%s:%s@%s",
			           User,
			           (sPwd || IS_FLAG(Opt.PwdSecurity,SEC_PLAINPWD)) ? Password : "",
			           Host);
		else
			str.printf("ftp://%s@%s",User,Host);
	}
	else
		str.printf("ftp://%s",Host);

	if(Path && *Path)
	{
		if(*Path != '/')
			str.Add('/');

		str.cat(Path);
	}

	if(nm && *nm)
	{
		int len = str.Length()-1;

		if(len >= 0 && str[len] != '/')
			str.Add('/');

		str.Add(nm);
	}

	FixFTPSlash(str);
}

char *FTPHost::MkINIFile(char *DestName,LPCSTR Path,LPCSTR DestPath)
{
	PROC(("FTPHost::MkINIFile","{%s,%s} [%s] [%s]",RegKey,Host,Path,DestPath))
	char *m,*m1;
	strcpy(DestName,DestPath);

	if(Path)
	{
		while(*Path == '/') Path++;

		AddEndSlash(DestName,'/',MAX_PATH);
		// Add from "Hosts\Folder\Item0" "Folder\Item0" part
		StrCat(DestName, RegKey + 6 /*the ARRAYSIZE("Hosts\\")*/ + strlen(Path), MAX_PATH);
		// Remove trailing "\Item0"
		m = strrchr(DestName,'/');

		if(m) *m = 0;
	}

	AddEndSlash(DestName,'/',MAX_PATH);
	//Correct bad characters and add possible to DestName
	BOOL  bad,
	   curBad;
	m   = DestName + strlen(DestName);    //ptr to add at
	m1  = HostName[0] ? HostName : Host;  //Source to translate
	bad = TRUE;                           //Do not add `bad` an start

	for(int i = (int)(m - DestName + 1);
	        i < MAX_PATH-4 && //Until not full Dest
	        *m1;                   //And source exist
	        m1++)
	{
		//Is bad
		curBad = ((BYTE)*m1) < ((BYTE)0x20) ||
		         strchr(Folder ? ":/\\\"\'|><^*?" : ":/\\.@\"\'|><^*?",*m1) != 0;

		//Already bad. Add only one.
		if(curBad)
		{
			if(!bad)
			{
				*m++ = '_';
				bad = TRUE;
				i++;
			}
		}
		else
		{
			//Add correct char
			*m++ = *m1;
			bad = FALSE;
			i++;
		}
	}

	*m = 0;

	//Add extension
	if(!Folder)
		strcat(DestName,".ftp");

	Log(("rc: [%s]",DestName));
	return DestName;
}

LPCSTR FTPHost::MkHost(LPCSTR Path,LPCSTR Name)
{
	PROC(("FTPHost::MkHost","[%s] [%s]",Path,Name))
	static char key[FAR_MAX_REG];
	StrCpy(key,"Hosts", ARRAYSIZE(key));

	if(Path)
	{
		if(StrNCmpI(Path,"Hosts",5) == 0)
			Path += 5;

		while(*Path == '/') Path++;

		if(Path[0])
		{
			AddEndSlash(key, '/',ARRAYSIZE(key));
			StrCat(key, Path, ARRAYSIZE(key));
		}
	}

	if(Name && Name[0])
	{
		while(*Name == '/') Name++;

		if(Name[0])
		{
			AddEndSlash(key, '/',ARRAYSIZE(key));
			StrCat(key, Name, ARRAYSIZE(key));
		}
	}

	DelEndSlash(key,'/');
	Log(("rc=%s",key));
	return key;
}

BOOL FTPHost::CheckHost(LPCSTR Path,LPCSTR Name)
{
	return FP_CheckRegKey(MkHost(Path,Name));
}

BOOL FTPHost::CheckHostFolder(LPCSTR Path,LPCSTR Name)
{
	LPCSTR m = MkHost(Path,Name);
	return FP_CheckRegKey(m) &&
	       FP_GetRegKey(m,"Folder",0) != 0;
}

char *FindLastBefore(char *str,char ch,char before)
{
	char *m,
	     *m1 = strchr(str,before);
	char *prev = str;

	while(true)
	{
		m  = strchr(prev,ch);

		if(!m || (m1 && m1 <= m))
			break;

		prev = m+1;
	}

	return prev == str ? NULL : (prev-1);
}

BOOL FTPHost::SetHostName(LPCSTR hnm,LPCSTR usr,LPCSTR pwd)
{
	PROC(("FTPHost::SetHostName","h:[%s], u:[%s], p:[%s]",hnm,usr,pwd))
	char *m = (char*)hnm,
	      *mHost;

	if(!hnm || !hnm[0])
		return FALSE;

	User[0]     = 0;
	Password[0] = 0;
	StrCpy(HostName,hnm,ARRAYSIZE(HostName));
//ftp.xx
	m = StrNCmpI(m,"ftp://",6) == 0 ? (m+6) : m;
	m = StrNCmpI(m,"http://",7) == 0 ? (m+7) : m;
	mHost = m;
	m = FindLastBefore(mHost,'@','/');

	if(m)
	{
//xx@ftp.xx
		StrCpy(Host,m+1,ARRAYSIZE(Host));
		StrCpy(User,mHost,Min((int)(m-mHost+1),(int)ARRAYSIZE(User)));
		m = FindLastBefore(User,':','/');

		if(m)
		{
//xx:xx@ftp.xx
			strcpy(Password,m+1);
			m[0] = 0;
		}
	}
	else
		strcpy(Host,mHost);

//Home
	m = strchr(Host,'/');

	if(m)
	{
		StrCpy(Home,m,ARRAYSIZE(Home));
		size_t Length = strlen(Home);

		if(Length>1 && Home[Length-1]=='/') Home[Length-1] = 0;

		*m = 0;
		FixFTPSlash(Home);
	}
	else
		Home[0] = 0;

//User
	if(usr && usr[0])
		StrCpy(User,usr,ARRAYSIZE(User));

//Psw
	if(pwd && pwd[0])
		StrCpy(Password,pwd,ARRAYSIZE(Password));

	return HostName[0] != 0;
}

void AddPath(char *buff,LPCSTR path)
{
	if(path && path[0])
	{
		AddEndSlash(buff,'/',MAX_PATH);

		while(*path == '/') path++;

		StrCat(buff,path,MAX_PATH);
	}

	DelEndSlash(buff,'/');
}

void FTPHost::MakeFreeKey(LPCSTR Hosts)
{
	PROC(("FTPHost::MakeFreeKey","%s",Hosts))
	char str[MAX_PATH];
	char key[MAX_PATH];
	int  n;
	strcpy(RegKey, MkHost(NULL,Hosts));
	Log(("BaseReg: [%s]",RegKey));

	if(Folder)
	{
		AddPath(RegKey,Host);
		Log(("folder rc: %s",RegKey));
		return;
	}

	for(n = 0; 1 ; n++)
	{
		StrCpy(key, RegKey,                ARRAYSIZE(key));
		StrCat(key, Message("/Item%d",n), ARRAYSIZE(key));
		FP_GetRegKey(key,"HostName",str,NULL,ARRAYSIZE(str));

		if(str[0] == 0)
		{
			strcpy(RegKey,key);
			break;
		}
		else
			Log(("Item [%s] exist",key));
	}

	Log(("host rc: %s",RegKey));
}
//---------------------------------------------------------------------------------
BOOL FTPHost::Read(LPCSTR nm)
{
	PROC(("FTPHost::Read","%s",nm))
	char *m;
	BYTE  psw[MAX_PATH];
	char  usr[MAX_PATH];
	char  hnm[MAX_PATH];
	char  pwd[MAX_PATH];
	//! Init called from `EnumHost::GetNextHost`
	//Init();

	if(nm && nm[0])
		StrCpy(RegKey,nm,ARRAYSIZE(RegKey));

	Log(("RegKey: %s",RegKey));
	Folder = FP_GetRegKey(RegKey,"Folder",0);

	if(Folder)
	{
		oldFmt       = FALSE;
		User[0]      = 0;
		Password[0]  = 0;
		Home[0]      = 0;
		FP_GetRegKey(RegKey,"Description", HostDescr, NULL,ARRAYSIZE(HostDescr));
		strcpy(Host,strrchr(RegKey,'/')+1);
		strcpy(HostName,Host);
		return TRUE;
	}

	m = strrchr(RegKey,'/');

	if(!m) m = RegKey;
	else m++;

	if(!FP_GetRegKey(RegKey,"HostName",hnm,"",ARRAYSIZE(hnm)))
		return FALSE;

	oldFmt = hnm[0] == 0;

	if(!oldFmt)
		m = hnm;

	FP_GetRegKey(RegKey, "User",     usr, NULL, ARRAYSIZE(usr));
	FP_GetRegKey(RegKey, "Password", psw, NULL, ARRAYSIZE(psw));

	if(psw[0])
		DecryptPassword(psw,pwd);
	else
		pwd[0] = 0;

	SetHostName(m,usr,pwd);
	FP_GetRegKey(RegKey,"Description",HostDescr, NULL,ARRAYSIZE(HostDescr));
	FP_GetRegKey(RegKey,"Table",      HostTable, NULL,ARRAYSIZE(HostTable));
	ProcessCmd    = FP_GetRegKey(RegKey,"ProcessCmd",        TRUE);
	AskLogin      = FP_GetRegKey(RegKey,"AskLogin",          FALSE);
	PassiveMode   = FP_GetRegKey(RegKey,"PassiveMode",       FALSE);
	UseFirewall   = FP_GetRegKey(RegKey,"UseFirewall",       FALSE);
	AsciiMode     = FP_GetRegKey(RegKey,"AsciiMode",         FALSE);
	ExtCmdView    = FP_GetRegKey(RegKey,"ExtCmdView",        Opt.ExtCmdView);
	ExtList       = FP_GetRegKey(RegKey,"ExtList",           FALSE);
	ServerType    = FP_GetRegKey(RegKey,"ServerType",        FTP_TYPE_DETECT);
	CodeCmd       = FP_GetRegKey(RegKey,"CodeCmd",           TRUE);
	FP_GetRegKey(RegKey,"ListCMD",ListCMD, "LIST -la",ARRAYSIZE(ListCMD));
	IOBuffSize    = FP_GetRegKey(RegKey,"IOBuffSize",        Opt.IOBuffSize);
	FFDup         = FP_GetRegKey(RegKey,"FFDup",             Opt.FFDup);
	UndupFF       = FP_GetRegKey(RegKey,"UndupFF",           Opt.UndupFF);
	DecodeCmdLine = FP_GetRegKey(RegKey,"DecodeCmdLine",     TRUE);
	SendAllo      = FP_GetRegKey(RegKey,"SendAllo",          FALSE);
	UseStartSpaces = FP_GetRegKey(RegKey,"UseStartSpaces", TRUE);
	IOBuffSize = Max((DWORD)FTR_MINBUFFSIZE,IOBuffSize);
	return TRUE;
}

BOOL FTPHost::Write(LPCSTR nm)
{
	PROC(("FTPHost::Write","%s",nm))
	BOOL rc;

	if(oldFmt &&
	        !WarnOldFormat(this))
		return FALSE;

	Log(("RegKey=[%s]",RegKey));

	RegWipeScope reg_wipe_scope;
	FP_DeleteRegKey(RegKey);

	if(!RegKey[0] || oldFmt)
		MakeFreeKey(nm);

	rc = TRUE;

	if(!Folder)
	{
		BYTE psw[ FTP_PWD_LEN ];
		Log(("pwd: [%s]", Password));

		if(Password[0])
			MakeCryptPassword(Password,psw);
		else
			psw[0] = 0;

		Log(("pwdc: [%s]", psw));
		rc = FP_SetRegKey(RegKey,"HostName",      HostName) &&
		     FP_SetRegKey(RegKey,"User",          User) &&
		     FP_SetRegKey(RegKey,"Password",      psw,ARRAYSIZE(psw)) &&
		     FP_SetRegKey(RegKey,"Table",         HostTable)      &&
		     FP_SetRegKey(RegKey,"AskLogin",      AskLogin)      &&
		     FP_SetRegKey(RegKey,"PassiveMode",   PassiveMode)      &&
		     FP_SetRegKey(RegKey,"UseFirewall",   UseFirewall)      &&
		     FP_SetRegKey(RegKey,"AsciiMode",     AsciiMode)      &&
		     FP_SetRegKey(RegKey,"ExtCmdView",    ExtCmdView)      &&
		     FP_SetRegKey(RegKey,"ExtList",       ExtList)         &&
		     FP_SetRegKey(RegKey,"ServerType",    ServerType)      &&
		     FP_SetRegKey(RegKey,"ListCMD",       ListCMD)         &&
		     FP_SetRegKey(RegKey,"ProcessCmd",    ProcessCmd)      &&
		     FP_SetRegKey(RegKey,"CodeCmd",       CodeCmd)         &&
		     FP_SetRegKey(RegKey,"IOBuffSize",    IOBuffSize)      &&
		     FP_SetRegKey(RegKey,"FFDup",         FFDup)           &&
		     FP_SetRegKey(RegKey,"UndupFF",       UndupFF)         &&
		     FP_SetRegKey(RegKey,"DecodeCmdLine", DecodeCmdLine)   &&
		     FP_SetRegKey(RegKey,"SendAllo",      SendAllo)        &&
		     FP_SetRegKey(RegKey,"UseStartSpaces", UseStartSpaces);
	}

	rc = rc &&
	     FP_SetRegKey(RegKey,"Description", HostDescr) &&
	     FP_SetRegKey(RegKey,"Folder",      Folder);
	return rc;
}
//---------------------------------------------------------------------------------

BOOL FTPHost::ReadINI(LPCSTR nm)
{
	PROC(("FTPHost::ReadINI","%s",nm))
	char   hex[MAX_PATH*2],
		hst[MAX_PATH],
	   usr[MAX_PATH],
	   pwd[MAX_PATH];
	HexToPassword_cb DecodeProc = NULL;
	Init();
	KeyFileReadSection kfh(nm, "FarFTP");
	
	kfh.GetChars(hst,ARRAYSIZE(hst), "Version");

	if(hst[0] == '1')
	{
		size_t sz;

		if(hst[1] == '.' && (sz=atoi(hst+2)) != 0)
			switch(sz)
			{
				case FTPHOST_DVERSION:
				case FTPHOST_DVERSION_SERVERTYPE:
				case FTPHOST_DVERSION_SERVERTYPE_CODE:
					DecodeProc = HexToPassword_2740;
					break;
			}
	}
	else
		DecodeProc = HexToPassword_OLD;

	if(!DecodeProc)
	{
		LPCSTR msgs[] =
		{
			FMSG(MError),
			FMSG(MNoVersion_txt1),
			FMSG(MNoVersion_txt2),
			FMSG(MOk)
		};
		FMessage(FMSG_WARNING,NULL,msgs,ARRAYSIZE(msgs),1);
		DecodeProc = HexToPassword_CUR;
	}

	kfh.GetChars(hst,ARRAYSIZE(hst),"Url");

	if(!hst[0])
		return FALSE;

	usr[0] = '1';
	usr[1] = 0;
	hex[0] = '2';
	hex[1] = 0;
	kfh.GetChars(usr, ARRAYSIZE(usr), "User");
	kfh.GetChars(hex, ARRAYSIZE(hex), "Password");

	if(!DecodeProc(hex,pwd) ||
	        !SetHostName(hst,usr,pwd))
		return FALSE;

	kfh.GetChars(HostDescr,ARRAYSIZE(HostDescr),"Description");
	AskLogin    = kfh.GetInt("AskLogin",  0);
	AsciiMode   = kfh.GetInt("AsciiMode",   0);
	PassiveMode = kfh.GetInt("PassiveMode", Opt.PassiveMode);
	UseFirewall = kfh.GetInt("UseFirewall", *Opt.Firewall!=0);
	ExtCmdView  = kfh.GetInt("ExtCmdView",  Opt.ExtCmdView);
	ExtList     = kfh.GetInt("ExtList",     FALSE);
	ServerType  = kfh.GetInt("ServerType",  Opt.ServerType);
	ProcessCmd  = kfh.GetInt("ProcessCmd",  TRUE);
	CodeCmd     = kfh.GetInt("CodeCmd",     TRUE);
	kfh.GetChars(ListCMD,ARRAYSIZE(ListCMD),"ListCMD");
	IOBuffSize  = kfh.GetInt("IOBuffSize",  Opt.IOBuffSize);
	FFDup       = kfh.GetInt("FFDup",       Opt.FFDup);
	UndupFF     = kfh.GetInt("UndupFF",     Opt.UndupFF);
	DecodeCmdLine = kfh.GetInt("DecodeCmdLine", TRUE);
	SendAllo    = kfh.GetInt("SendAllo",    FALSE);
	UseStartSpaces = kfh.GetInt("UseStartSpaces",    TRUE);
	kfh.GetChars(HostTable,ARRAYSIZE(HostTable),"CharTable");
	IOBuffSize = Max((DWORD)FTR_MINBUFFSIZE,IOBuffSize);
	return TRUE;
}
BOOL FTPHost::WriteINI(LPCSTR nm)
{
	PROC(("FTPHost::WriteINI","%s",nm))
	char HexStr[MAX_PATH*2];
	BOOL res;
//CreateDirectory
	char *m = (char*)strrchr(nm,'/'); // BUGBUG

	if(m)
	{
		StrCpy(HexStr,nm,ARRAYSIZE(HexStr));
		m = strrchr(HexStr,'/');
		*m = 0;

		if(!DoCreateDirectory(HexStr))
			return FALSE;
	}

//Write INI
	KeyFileHelper kfh(nm, false);
	//WritePrivateProfileString(NULL,NULL,NULL,nm);
	PasswordToHex(Password,HexStr);
	kfh.SetString("FarFTP","Version",  FTPHOST_VERSION);
	kfh.SetString("FarFTP","Version",  FTPHOST_VERSION);
	kfh.SetString("FarFTP","Version",            FTPHOST_VERSION);
	kfh.SetString("FarFTP","Url",                HostName) ;
	kfh.SetString("FarFTP","User",               User);
	kfh.SetString("FarFTP","Password",           HexStr);
	kfh.SetString("FarFTP","Description",        HostDescr);
	kfh.SetInt("FarFTP","AskLogin",           AskLogin);
	kfh.SetInt("FarFTP","AsciiMode",          AsciiMode);
	kfh.SetInt("FarFTP","PassiveMode",        PassiveMode);
	kfh.SetInt("FarFTP","UseFirewall",        UseFirewall);
	kfh.SetInt("FarFTP","ExtCmdView",         ExtCmdView);
	kfh.SetInt("FarFTP","ExtList",            ExtList);
	kfh.SetInt("FarFTP","ServerType",         ServerType);
	kfh.SetInt("FarFTP","CodeCmd",            CodeCmd);
	kfh.SetString("FarFTP","ListCMD",            "LIST -la");
	kfh.SetInt("FarFTP","IOBuffSize",         IOBuffSize);
	kfh.SetInt("FarFTP","FFDup",              FFDup);
	kfh.SetInt("FarFTP","UndupFF",            UndupFF);
	kfh.SetInt("FarFTP","DecodeCmdLine",      DecodeCmdLine);
	kfh.SetInt("FarFTP","SendAllo",           SendAllo);
	kfh.SetInt("FarFTP","UseStartSpaces",  UseStartSpaces);
	kfh.SetInt("FarFTP","ProcessCmd",         ProcessCmd);
	kfh.SetString("FarFTP","CharTable", HostTable);

	//if(res)
	//	WritePrivateProfileString(NULL,NULL,NULL,nm);

	return TRUE;
}
