/***************************************************************************
 *   Copyright (C) 2004 by Magnus Kulke                                    *
 *   mkulke@magnusmachine                                                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

// enable > 2gb support (LFS)

#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE 
 
#include <kstandarddirs.h>

#include <tqapplication.h>
#include <tqevent.h>
#include <tqdir.h>

#include <iostream>
#include <list>

#include "kbconfig.h"
#include "ftplib.h"
#include "kbdirinfo.h"
#include "eventhandler.h"
#include "kbfileinfo.h"
#include "ftpthread.h"

using namespace std;

/* 
class description:

the class accepts ftp operations by public methods. when such a method is called,
a certain type value is appended to a list. in the threads main loop (triggered when 
start() is called) the list is processed by calling the ftplibpp functions. depending
on the result of an operation a certain event is posted to the eventreceiver. when
there is a value to be returned, it's named out_. 
*/


FtpThread::FtpThread() : TQThread()
{
	mp_eventreceiver = NULL;
	mp_ftp = new ftplib();
	
	mp_ftp->SetCallbackArg(this);
	mp_ftp->SetCallbackLogFunction(FtpThread::CallbackLog);
	mp_ftp->SetCallbackBytes(1024);
	mp_ftp->SetCallbackXferFunction(FtpThread::CallbackXfer);
}

FtpThread::~FtpThread()
{
	delete mp_ftp;
}

/* init the variables the ftp session needs */

void FtpThread::InitInternals()
{
	m_stringlist.clear();
	m_intlist.clear();
	m_ulonglist.clear();
	m_pwd = "";
	m_dataencrypted = false;
	m_cache_vector.clear();
	m_cache_list.clear();
}

/* callback function for the transfer */

int FtpThread::CallbackXfer(off64_t xfered, void *arg)
{
	FtpThread* ftp = static_cast<FtpThread*>(arg);
		
	ftp->Event(EventHandler::xfered, new xferpair(xfered, ftp->m_dataencrypted));
	return 1;
}

/* callback function for the logs */

void FtpThread::CallbackLog(char *log, void *arg, bool out)
{
	/* the incoming log message isn't formatted at all. data
	can be "end.\r\n230 ne" for example. the log gets chopped 
	into lines and for every line an events is posted. for 
	in-logs there's no need for this procedure, as the logs
	always arrive in line-format. */ 

	FtpThread* ftp = static_cast<FtpThread*>(arg);	
		
	if (out)
	{	
		int pos;
		TQString buffer = ftp->m_linebuffer + log;
		while ((pos = buffer.find('\n')) != -1)
		{
			ftp->Event(EventHandler::outlog, new TQString(buffer.left(pos + 1)));
			buffer.remove(0, pos + 1);
		}
		ftp->m_linebuffer = buffer;
	}
	else ftp->Event(EventHandler::inlog, new TQString(log));
}

/* set the receiver for the events the thread posts when a certain ftp operation is done */

void FtpThread::SetEventReceiver(TQObject* eventreceiver)
{
	mp_eventreceiver = eventreceiver;
}

/* connect to host */

bool FtpThread::Connect(TQString host)
{
	InitInternals();

	if (running()) return false;
	else
	{	
		m_stringlist.append(host);
		m_tasklist.append(FtpThread::connect);
		return true;
	}
}

/* login with user and pass */

bool FtpThread::Login(TQString user, TQString pass)
{
	if (running()) return false;
	else
	{
		m_stringlist.append(user);
		m_stringlist.append(pass);
		m_tasklist.append(FtpThread::login);
		return true;
	}
}

/* quit the ftp session*/

bool FtpThread::Quit()
{
	if (running()) return false;
	else
	{
		m_tasklist.append(FtpThread::quit);
		return true;
	}
}

/* get file */

bool FtpThread::Transfer_Get(TQString src, TQString dst, int tls, off64_t resume)
{
	if (running()) return false;
	else
	{
		m_stringlist.append(src);
		m_stringlist.append(dst);
		m_intlist.append(tls);
		m_ulonglist.append(resume);
		m_tasklist.append(FtpThread::transfer_get);
		return true;
	}
}

/* fxp file */

bool FtpThread::Transfer_Fxp(TQString src, TQString dst, FtpThread *dstftp, int srctls, int dsttls, off64_t resume, int alt)
{
	if (running()) return false;
	else
	{
		m_stringlist.append(src);
		m_stringlist.append(dst);
		m_ftplist.append(dstftp);
		m_intlist.append(srctls);
		m_intlist.append(dsttls);
		m_ulonglist.append(resume);
		m_intlist.append(alt);	
		m_tasklist.append(FtpThread::transfer_fxp);
		return true;
	}
}

/* put file */

bool FtpThread::Transfer_Put(TQString src, TQString dst, int tls, off64_t resume)
{
	if (running()) return false;
	else
	{
		m_stringlist.append(src);
		m_stringlist.append(dst);
		m_intlist.append(tls);
		m_ulonglist.append(resume);
		m_tasklist.append(FtpThread::transfer_put);
		return true;
	}
}

/* transfer mkdir */

bool FtpThread::Transfer_Mkdir(TQString dir)
{
	if (running()) return false;
	else
	{
		m_stringlist.append(dir);
		m_tasklist.append(FtpThread::transfer_mkdir);
		return true;
	}
}

/* rename file */

bool FtpThread::Rename(TQString src, TQString dst)
{
	if (running()) return false;
	else
	{
		m_stringlist.append(src);
		m_stringlist.append(dst);
		m_tasklist.append(FtpThread::rename);
		return true;
	}
}

/* set wether the data channel is encrypted or not */

bool FtpThread::Pasv(bool flag)
{
	if (running()) return false;
	else
	{
		if (flag) mp_ftp->SetConnmode(ftplib::pasv);
		else mp_ftp->SetConnmode(ftplib::port);
		return true;
	}
}

/* set wether the data channel is encrypted or not */

bool FtpThread::EncryptData(bool flag, bool force)
{
	if (running()) return false;
	else
	{	
		m_intlist.append(flag);
		m_intlist.append(force);
		m_tasklist.append(FtpThread::dataencryption);
		return true;
	}
}

/* retrieve the current workind dir */

bool FtpThread::Pwd()
{
	if (running()) return false;
	else
	{
		m_tasklist.append(FtpThread::pwd);
		return true;
	}
}

/* change working dir to the given path */

bool FtpThread::Chdir(TQString path)
{
	if (running()) return false;
	else
	{
		m_stringlist.append(path);
		m_tasklist.append(FtpThread::chdir);
		return true;
	}
}

/* change working dir to the given path, used by transfers */

bool FtpThread::Transfer_Changedir(TQString dir, int tls)
{
	if (running()) return false;
	else
	{
		m_stringlist.append(dir);
		m_intlist.append(tls);
		m_tasklist.append(FtpThread::transfer_changedir);
		return true;
	}
}

/* creates a directory */

bool FtpThread::Mkdir(TQString path)
{
	if (running()) return false;
	else
	{
		m_stringlist.append(path);
		m_tasklist.append(FtpThread::mkdir);
		return true;
	}
}

/* step above in the working dir */

bool FtpThread::Cdup()
{
	if (running()) return false;
	else
	{
		m_tasklist.append(FtpThread::cdup);
		return true;
	}
}

/* retrieve dir contents */

bool FtpThread::Dir(bool force)
{
	if (running()) return false;
	else
	{
		m_intlist.append(force);
		m_tasklist.append(FtpThread::dir);
		return true;
	}
}

/* attempt negotiating an encrypted session */

bool FtpThread::Authtls()
{
	if (running()) return false;
	else
	{
		m_tasklist.append(FtpThread::authtls);
		return true;
	}
}

/* delete a file  */

bool FtpThread::Rm(TQString name)
{
	wait(KB_THREAD_TIMEOUT);
	if (running()) return false;
	else
	{
		m_stringlist.append(name);
		m_tasklist.append(FtpThread::rm);
		return true;
	}
}

/* delete a directory  */

bool FtpThread::Rmdir(TQString name)
{
	if (running()) return false;
	else
	{
		m_stringlist.append(name);
		m_tasklist.append(FtpThread::rmdir);
		return true;
	}
}
	
/* issue raw command  */

bool FtpThread::Raw(TQString cmd)
{
	if (running()) return false;
	else
	{
		m_stringlist.append(cmd);
		m_tasklist.append(FtpThread::raw);
		return true;
	}
}

/* scan the directory recursively */

bool FtpThread::Scandir(KbDirInfo* dir)
{
	if (running()) return false;
	else
	{
		mp_scandir = dir;
		m_tasklist.append(FtpThread::scandir);
		return true;
	}
}
/* the thread methods */

void FtpThread::Connect_thread()
{
	int result;
	TQString host = m_stringlist.front();
	m_stringlist.pop_front();

	result = mp_ftp->Connect(host.latin1());

	//unsigned long int xxx = mp_ftp->mp_netbuf->ipappr;
	//tqWarning("INFO %lu.%lu.%lu.%lu", xxx & 0xff, (xxx >> 8) & 0xff, (xxx >> 16) & 0xff, (xxx >> 24) & 0xff); 
	//tqWarning("INFO: ip:%lu", mp_ftp->mp_netbuf->ipappr);
	
	if (result) Event(EventHandler::connect_success);
	else 
	{
		ClearQueue();
		Event(EventHandler::connect_failure);
	}
}

void FtpThread::Authtls_thread()
{
	int result;
	
	result = mp_ftp->NegotiateEncryption();

	if (result) Event(EventHandler::authtls_success);
	else 
	{
		ClearQueue();
		Event(EventHandler::authtls_failure);
	}
}

void FtpThread::Login_thread()
{
	int result;
	TQString user = m_stringlist.front();
	m_stringlist.pop_front();
	TQString pass = m_stringlist.front();
	m_stringlist.pop_front();

	result = mp_ftp->Login(user.latin1(), pass.latin1());
	
	if (result) Event(EventHandler::login_success);
	else
	{
		ClearQueue();
		Event(EventHandler::login_failure);
	}
}

void FtpThread::Dataencryption_thread()
{
	int result, flag, force, cacheindex;

	flag = m_intlist.front();
	m_intlist.pop_front();
	force = m_intlist.front();
	m_intlist.pop_front();
	
	cacheindex = -1;
	
	if (KbConfig::dirCachingIsEnabled())
	{
		cacheindex = m_cache_list.findIndex(m_pwd);
	
		if ((cacheindex != -1) && (!force))
		{
			Event(EventHandler::encryptdata_success);
			return;
		}
	}
	
	if (flag) result = mp_ftp->SetDataEncryption(ftplib::secure);
	else result = mp_ftp->SetDataEncryption(ftplib::unencrypted);
	
	if (result) 
	{
		Event(EventHandler::encryptdata_success);
		m_dataencrypted = flag;
	}
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::encryptdata_failure);
	}	
}

void FtpThread::Transfer_Changedir_thread()
{
	int result;
	char buffer[1024];
	TQString dirname;
	
	dirname = locateLocal("appdata", TQString::number(rand()) + ".dir");
				
	TQString path = m_stringlist.front();
	m_stringlist.pop_front();
	int tls = m_intlist.front();
	m_intlist.pop_front();
	
	if (path == m_pwd)
	{
		Event(EventHandler::transfer_success);
		return;	
	}
	
	result = mp_ftp->Chdir(path.latin1());
	if (result) Event(EventHandler::chdir_success);
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::chdir_failure);	
		Event(EventHandler::transfer_failure);
		return;
	}

	result = mp_ftp->Pwd(buffer, 1024);
	if (result) 
	{
		Event(EventHandler::pwd_success, new TQString(buffer));
		m_pwd = buffer;
	}
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::pwd_failure);
		Event(EventHandler::transfer_failure);
		return;
	}
	
	if (tls)
	{
		result = mp_ftp->SetDataEncryption(ftplib::secure);
		if (result) 
		{
			Event(EventHandler::encryptdata_success);
			m_dataencrypted = true;
		}
		else 
		{
			if (ConnectionLost()) Event(EventHandler::connectionlost);
			else Event(EventHandler::encryptdata_failure);
			Event(EventHandler::transfer_failure);
			return;
		}
	}
		
	result = mp_ftp->Dir(dirname.latin1(), "");
	if (result) 
	{		
		m_dirlist.clear();
		m_filelist.clear();		
		FormatFilelist(dirname.latin1(), m_pwd, &m_dirlist, &m_filelist); 
		m_dircontent.first = m_dirlist;
		m_dircontent.second = m_filelist;
		Event(EventHandler::dir_success, &m_dircontent);
	}
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::dir_failure, &m_dircontent);
	}
	
	if (!result) Event(EventHandler::transfer_failure);
	else Event(EventHandler::transfer_success);
	
	TQFile::remove(dirname);	
}

void FtpThread::Transfer_Mkdir_thread()
{
	int result;
	TQString dir = m_stringlist.front();
	m_stringlist.pop_front();
	
	result = mp_ftp->Mkdir(dir.latin1());
	if (result) Event(EventHandler::mkdir_success);
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::mkdir_failure);	
		Event(EventHandler::transfer_failure);
		return;
	}
	
	Event(EventHandler::transfer_success);
}

void FtpThread::Transfer_Get_thread()
{
	int result;
	
	TQString src = m_stringlist.front();
	m_stringlist.pop_front();
	TQString dst = m_stringlist.front();
	m_stringlist.pop_front();
	int tls = m_intlist.front();
	m_intlist.pop_front();
	off64_t resume = m_ulonglist.front();
	m_ulonglist.pop_front();
		
	if (tls > 1)
	{
		if (tls == 2) result = mp_ftp->SetDataEncryption(ftplib::unencrypted);
		else result = mp_ftp->SetDataEncryption(ftplib::secure);
		if (result) 
		{
			Event(EventHandler::encryptdata_success);
			m_dataencrypted = (tls > 2);
		}
		else 
		{
			if (ConnectionLost()) Event(EventHandler::connectionlost);
			else Event(EventHandler::encryptdata_failure);
			Event(EventHandler::transfer_failure);
			return;
		}
	}
	
	if (resume == 0) result = mp_ftp->Get(dst.latin1(), src.latin1(), ftplib::image);
	else result = mp_ftp->Get(dst.latin1(), src.latin1(), ftplib::image, resume);
	
	if (result) Event(EventHandler::get_success);
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::get_failure);
		Event(EventHandler::transfer_failure);
		return;
	}
	
	Event(EventHandler::transfer_success);
}

void FtpThread::Transfer_Put_thread()
{
	int result;
	
	TQString src = m_stringlist.front();
	m_stringlist.pop_front();
	TQString dst = m_stringlist.front();
	m_stringlist.pop_front();
	int tls = m_intlist.front();
	m_intlist.pop_front();
	off64_t resume = m_ulonglist.front();
	m_ulonglist.pop_front();
	
	if (tls > 1)
	{
		if (tls == 2) result = mp_ftp->SetDataEncryption(ftplib::unencrypted);
		else result = mp_ftp->SetDataEncryption(ftplib::secure);
		if (result) 
		{
			Event(EventHandler::encryptdata_success);
			m_dataencrypted = (tls > 2);
		}
		else 
		{		
			if (ConnectionLost()) Event(EventHandler::connectionlost);
			else Event(EventHandler::encryptdata_failure);
			Event(EventHandler::transfer_failure);
			return;
		}
	}
	
	if (resume == 0) result = mp_ftp->Put(src.latin1(), dst.latin1(), ftplib::image);
	else result = mp_ftp->Put(src.latin1(), dst.latin1(), ftplib::image, resume);

	if (result) Event(EventHandler::put_success);
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::put_failure);
		Event(EventHandler::transfer_failure);
		return;
	}
	
	Event(EventHandler::transfer_success);
}

void FtpThread::Quit_thread()
{
	int result;
	
	result = mp_ftp->Quit();
	
	if (result) Event(EventHandler::quit_success);
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::quit_failure);
	}
}

void FtpThread::Transfer_Fxp_thread()
{
	int result = 0;
	ftplib::ftp method;
	
	TQString src = m_stringlist.front();
	m_stringlist.pop_front();
	TQString dst = m_stringlist.front();
	m_stringlist.pop_front();
	FtpThread* dstftp = m_ftplist.front();
	m_ftplist.pop_front();
	int srctls = m_intlist.front();
	m_intlist.pop_front();
	int dsttls = m_intlist.front();
	m_intlist.pop_front();
	off64_t resume = m_ulonglist.front();
	m_ulonglist.pop_front();
	int alt = m_intlist.front();
	m_intlist.pop_front();
	
	if (srctls > 1)
	{
		if (!FxpDisableTls())
		{
			Event(EventHandler::transfer_failure);
			return;
		}
	}
	
	if (dsttls > 1)
	{
		if (!dstftp->FxpDisableTls())
		{
			Event(EventHandler::transfer_failure);
			return;
		}
	}
	
	if (alt) method = ftplib::alternativefxp;
	else method = ftplib::defaultfxp;
	
	tqWarning("bla_thread: %d", alt);
	
	if (resume) tqWarning("WARNING: fxp resume isn't supported. overwriting file instead");
	result == ftplib::Fxp(mp_ftp, dstftp->Ftp(), src.utf8(), dst.utf8(), ftplib::image, method);
	
	FxpReportResult(result);
	dstftp->FxpReportResult(result);
	
	if (result) Event(EventHandler::transfer_success);
	else Event(EventHandler::transfer_failure);
}

bool FtpThread::FxpDisableTls()
{
	bool result = mp_ftp->SetDataEncryption(ftplib::unencrypted);
	if (result) 
	{
		Event(EventHandler::encryptdata_success);
		m_dataencrypted = false;
	}
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::encryptdata_failure);
	}
	
	return result;
}

void FtpThread::FxpReportResult(bool result)
{
	if (result) Event(EventHandler::fxp_success);
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::fxp_failure);
	}	
}

void FtpThread::Pwd_thread()
{
	char buffer[1024];
	int result;
	
	result = mp_ftp->Pwd(buffer, 1024);
	
	if (result) 
	{
		Event(EventHandler::pwd_success, new TQString(buffer));
		m_pwd = buffer;
	}
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::pwd_failure);
	}
}

void FtpThread::Chdir_thread()
{
	int result;
	
	TQString path = m_stringlist.front();
	m_stringlist.pop_front();
	
	result = mp_ftp->Chdir(path.latin1());

	if (result) Event(EventHandler::chdir_success);
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::chdir_failure);
	}
}

void FtpThread::Mkdir_thread()
{
	int result;
	
	TQString path = m_stringlist.front();
	m_stringlist.pop_front();
	
	result = mp_ftp->Mkdir(path.latin1());

	if (result) Event(EventHandler::mkdir_success);
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::mkdir_failure);
	}
}

void FtpThread::Cdup_thread()
{
	int result;
	
	result = mp_ftp->Cdup();

	if (result) Event(EventHandler::chdir_success);
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::chdir_failure);
	}
}

void FtpThread::Rm_thread()
{
	int result;
	
	TQString name = m_stringlist.front();
	m_stringlist.pop_front();
	
	result = mp_ftp->Delete(name.latin1());

	if (result) Event(EventHandler::rm_success);
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::rm_failure);
	}
}

void FtpThread::Rename_thread()
{
	int result;
	
	TQString src = m_stringlist.front();
	m_stringlist.pop_front();
	TQString dst = m_stringlist.front();
	m_stringlist.pop_front();
	
	result = mp_ftp->Rename(src.latin1(),dst.latin1());

	if (result) Event(EventHandler::rename_success);
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::rename_failure);
	}
}

void FtpThread::Dir_thread()
{
	int result;
	TQString dirname;
	int cacheindex = -1;
	
	int force = m_intlist.front();
	m_intlist.pop_front();
	 	
	dirname = locateLocal("appdata", TQString::number(rand()) + ".dir");
	
	if (KbConfig::dirCachingIsEnabled())
	{
		cacheindex = m_cache_list.findIndex(m_pwd);
	
		if ((cacheindex != -1) && (!force))
		{
			m_dircontent = m_cache_vector.at(cacheindex);
			Event(EventHandler::dir_success, &m_dircontent);
			return;
		}
	}
				
	result = mp_ftp->Dir(dirname.latin1(), "");

	if (result) 
	{		
		m_dirlist.clear();
		m_filelist.clear();		
		FormatFilelist(dirname.latin1(), m_pwd, &m_dirlist, &m_filelist); 
		m_dircontent.first = m_dirlist;
		m_dircontent.second = m_filelist;
		m_cache_vector.push_back(m_dircontent);
		m_cache_list.push_back(m_pwd);
		
		if (KbConfig::dirCachingIsEnabled())
		{	
			if (cacheindex == -1)
			{
				m_cache_vector.push_back(m_dircontent);
				m_cache_list.push_back(m_pwd);
			}
			else m_cache_vector.at(cacheindex) = m_dircontent;
		}
		Event(EventHandler::dir_success, &m_dircontent);
	}
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::dir_failure, &m_dircontent);
	}
	
	TQFile::remove(dirname);
}

void FtpThread::Scandir_thread()
{
	bool result = true;

	list<KbDirInfo*>::iterator end_dir = mp_scandir->Dirlist()->end();
	for(list<KbDirInfo*>::iterator dirIterator = mp_scandir->Dirlist()->begin(); dirIterator != end_dir; dirIterator++)
	{
		result = Scandir_recurse(*dirIterator, (*dirIterator)->dirPath() + (*dirIterator)->fileName());
	}
	
	if (result) Event(EventHandler::scandir_success, mp_scandir);
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::scandir_failure);
	}
}

void FtpThread::Rmdir_thread()
{
	bool result;
	
	TQString name = m_stringlist.front();
	m_stringlist.pop_front();
	
	result = Delete_recurse(name.latin1());

	if (result) Event(EventHandler::rmdir_success);
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::rmdir_failure);
	}
}

void FtpThread::Raw_thread()
{
	bool result;
	
	TQString cmd = m_stringlist.front();
	m_stringlist.pop_front();
	
	result = mp_ftp->Raw(cmd.latin1());

	if (result) Event(EventHandler::raw_success);
	else 
	{
		if (ConnectionLost()) Event(EventHandler::connectionlost);
		else Event(EventHandler::raw_failure);
	}
}

/* the thread's main loop */

void FtpThread::run()
{
	while (!m_tasklist.empty())
	{
		task t = m_tasklist.front();
		m_tasklist.pop_front();
		
		switch (t)
		{
			case FtpThread::connect:
				Connect_thread();
				break;
			case FtpThread::login:
				Login_thread();
				break;
			case FtpThread::quit:
				Quit_thread();
				break;
			case FtpThread::pwd:
				Pwd_thread();
				break;
			case FtpThread::chdir:
				Chdir_thread();
				break;
			case FtpThread::cdup:
				Cdup_thread();
				break;
			case FtpThread::dir:
				Dir_thread();
				break;
			case FtpThread::scandir:
				Scandir_thread();
				break;
			case FtpThread::rm:
				Rm_thread();
				break;
			case FtpThread::rmdir:
				Rmdir_thread();
				break;
			case FtpThread::authtls:
				Authtls_thread();
				break;
			case FtpThread::dataencryption:
				Dataencryption_thread();
				break;
			case FtpThread::transfer_get:
				Transfer_Get_thread();
				break;
			case transfer_put:
				Transfer_Put_thread();
				break;
			case FtpThread::mkdir:
				Mkdir_thread();
				break;
			case FtpThread::rename:
				Rename_thread();
				break;
			case FtpThread::raw:
				Raw_thread();
				break;
			case FtpThread::transfer_changedir:
				Transfer_Changedir_thread();
				break;
			case FtpThread::transfer_mkdir:
				Transfer_Mkdir_thread();
				break;
			case FtpThread::transfer_fxp:
				Transfer_Fxp_thread();
			default:
				Event(EventHandler::error);
				break;
		}
	}
	Event(EventHandler::finished);
}

/* event is posted to the eventreceiver */

void FtpThread::Event(EventHandler::EventType type, void *data)
{
	if (mp_eventreceiver == NULL) tqWarning("WARNING: mp_eventreceiver is NULL");
	else 
	{
		TQCustomEvent* e = new TQCustomEvent(type);
		if (data != NULL) e->setData(data);	
		tqApp->postEvent(mp_eventreceiver, e);	
	}
}

/* parses the dir file */

bool FtpThread::FormatFilelist(const char *filename, TQString current, filist *dirtable, filist *filetable)
{
	int i, blocks, space, month_int = 1, loc;
	unsigned int fileloc, datebegin, sizebegin = 0;
	string buffer, filestring;

	FILE* dirfile;

	char file[1024];

	char month[][5] = {
		"... ", "Jan ", "Feb ", "Mar ", "Apr ", "May ", "Jun ", "Jul ", "Aug ",
		"Sep ", "Oct ", "Nov ", "Dec "
	};

	dirfile = fopen(filename, "r");
	if (dirfile == NULL)
	{
		tqWarning("ERROR: failed open dirfile");
		return false;
	}

	while (fgets(file, 1024, dirfile) != NULL)
	{
		*(strchr(file,'\n')) = '\0';
		buffer = file;

		for (i = 1; i < 13; i++)
		{
			loc = buffer.find(month[i], 0);
			if (loc != string::npos)
			{
				month_int = i;
				break;
			}
		}
		if( loc == string::npos )
		{
			tqWarning("INFO: no month entry found");
			loc = buffer.length();
		}

		datebegin = loc;

		space = 1;
		blocks = 0;
		fileloc = 0;
		while (loc < buffer.length())
		{
				if (space)
				{
					// look for a space
					if (buffer[loc] == ' ')
					{
						blocks++;
						space = !space;
					}
				}
				else
				{
					// look for a non-space
					if (buffer[loc] != ' ')
					{
						if (blocks > 2)
						{
							fileloc = loc;
							break;
						}
						space = !space;
					}
				}
				loc += 1;
		}
		if (blocks != 3)
		{
			tqWarning("INFO: ignoring invalid line in dirlist");
		}
		else
		{
			TQString date(buffer.substr( datebegin, loc - datebegin - 1).c_str());

			int day_int = date.section( ' ', 1, 1 ).toInt();
			int year_int = date.section( ' ', -1, -1 ).toInt();
			int hour_int = 0;
			int minute_int = 0;
			
			if (day_int == 0) day_int = date.section( ' ', 1, 2 ).toInt();

			if (year_int == 0)
			{
				year_int = TQDate::currentDate().year();
				if (month_int > TQDate::currentDate().month()) year_int--;
				TQTime mytime = TQTime::fromString(date.section( ' ', -1, -1 ));
				hour_int = mytime.hour();
				minute_int = mytime.minute();
			}

			unsigned int date_int = 
				(minute_int) |
				(hour_int << 6 ) |
				(day_int << 11) |
				(month_int << 16) |
				(year_int << 20);
			
			// 6 bits for minutes, 5 bits for hours, 5 bits for days, 4 bits for months, 11 bits for years -> 31 bits (should be sufficient)
			
			space = 0;
			blocks = 0;
			loc = datebegin;
			while (loc > 0)
			{
				if (space)
				{
					if (buffer[loc] == ' ')
					{
						blocks++;
						space = !space;
					}
				}
				else
				{
					if (buffer[loc] != ' ')
					{
						if (blocks > 1)
						{
							sizebegin = loc + 1;
							break;
						}
						space = !space;
					}
				}
				loc--;
			}

			off64_t size = atoll(buffer.substr( sizebegin, datebegin - sizebegin).c_str());
			
			filestring.erase();
			filestring.append(buffer, fileloc, buffer.length() - fileloc);
			if ((filestring.compare(".") != 0) && (filestring.compare("..") != 0))
			{
				if ((*file == 'd') || (*file == 'D'))
				{
					KbFileInfo* di = new KbFileInfo(current, filestring.c_str(), size, date, date_int);
					dirtable->push_back(di);
				}
				else if ((*file == 'l') || (*file == 'L'))
				{
					tqWarning("INFO: links to files not supported yet");
				}
				else
				{
					KbFileInfo* fi = new KbFileInfo(current, filestring.c_str(), size, date, date_int);
					filetable->push_back(fi);
				}
			}
		}
	}
	fclose(dirfile);
	return true;
}

/* recursive method to check content of a directory */

bool FtpThread::Scandir_recurse(KbDirInfo *dir, TQString path)
{	
	char currentpath[1024];
	int result;
	filist dirlist;
	filist filelist;
	TQString dirname;
		
	result = mp_ftp->Pwd(currentpath, 1024);
	
	if (!result) 
	{
		Event(EventHandler::misc_failure);
		return false;
	}
	else Event(EventHandler::misc_success);
	
	result = mp_ftp->Chdir(dir->fileName().latin1());
	
	if (!result) 
	{
		Event(EventHandler::misc_failure);
		return true;
	}
	else Event(EventHandler::misc_success);
	
	dirname = locateLocal("appdata", TQString::number(rand()) + ".dir");
	
	result = mp_ftp->Dir(dirname.latin1(), "");
			
	if (!result) 
	{
		Event(EventHandler::misc_failure);
		return false;
	}
	else Event(EventHandler::misc_success);
	
	if(!FormatFilelist(dirname.latin1(), path, &dirlist, &filelist)) return false; 
	
	TQFile::remove(dirname);
	
	filist::iterator end_file = filelist.end();	
	for(filist::iterator fiIterator = filelist.begin(); fiIterator != end_file; fiIterator++)
	{
		dir->AddFile(*fiIterator);
	}
	
	filist::iterator end_dir = dirlist.end();
	for(filist::iterator fiIterator = dirlist.begin(); fiIterator != end_dir; fiIterator++)
	{
		KbDirInfo* newdir = dir->AddDirectory(**fiIterator);
		if (!Scandir_recurse(newdir, path + '/' + newdir->fileName())) return false;
	}
		
	result = mp_ftp->Chdir(currentpath);
	
	if (!result) 
	{
		Event(EventHandler::misc_failure);
		return false;
	}
	else Event(EventHandler::misc_success);
	
	return true;
}

/* recursive method to delete directories */

bool FtpThread::Delete_recurse(TQString name)
{
	char currentdir[1024];
	int result;
	filist dirlist;
	filist filelist;
	TQString dirname;
	
	result = mp_ftp->Pwd(currentdir, 1024);
	
	if (!result) 
	{
		Event(EventHandler::misc_failure);
		return false;
	}
	else Event(EventHandler::misc_success);
		
	result = mp_ftp->Chdir(name.latin1());
	
	if (!result) 
	{
		Event(EventHandler::misc_failure);
		return true;
	}
	else Event(EventHandler::misc_success);
	
	//dirname = TQDir::homeDirPath() + 
	//"/.kasablanca/" +
	//TQString::number((int) time(NULL) & 0xffff) + 
	//".dir";
	dirname = locateLocal("appdata", TQString::number(rand()) + ".dir");
	
	result = mp_ftp->Dir(dirname.latin1(), "");
			
	if (!result) 
	{
		Event(EventHandler::misc_failure);
		return false;
	}
	else Event(EventHandler::misc_success);
	
	if(!FormatFilelist(dirname.latin1(), "", &dirlist, &filelist)) return false; 
	
	TQFile::remove(dirname);
	
	filist::iterator end_file = filelist.end();
	for(filist::iterator fiIterator = filelist.begin(); fiIterator != end_file; fiIterator++)
	{
		result = mp_ftp->Delete((*fiIterator)->fileName().latin1());
		if (!result) 
		{
			Event(EventHandler::misc_failure);
			return false;
		}
		else Event(EventHandler::misc_success);
	}
	
	filist::iterator end_dir = dirlist.end();
	for(filist::iterator fiIterator = dirlist.begin(); fiIterator != end_dir; fiIterator++)
	{
		if (!Delete_recurse((*fiIterator)->fileName())) return false;
	}
		
	result = mp_ftp->Chdir(currentdir);
	
	if (!result) 
	{
		Event(EventHandler::misc_failure);
		return false;
	}
	else Event(EventHandler::misc_success);
	
	result = mp_ftp->Rmdir(name.latin1());
	
	if (!result) 
	{
		Event(EventHandler::misc_failure);
		return false;
	}
	else Event(EventHandler::misc_success);
	
	return true;
}

/* check if we lost the connection */

bool FtpThread::ConnectionLost()
{
	TQString response;
	
	response = mp_ftp->LastResponse();

	if (response.startsWith("421"))
	{
		ClearQueue();
		return true;
	}
	else return false;
}
	
/* empty queue */

void FtpThread::ClearQueue()
{
	m_tasklist.clear();
	m_stringlist.clear();
	m_intlist.clear();
	m_ulonglist.clear();
	m_ftplist.clear();
	m_cache_vector.clear();
	m_cache_list.clear();
}
