/* KPilot
 **
 ** Copyright (C) 1998-2001 by Dan Pilone
 ** Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
 ** Copyright (C) 2006-2007 Adriaan de Groot <groot@kde.org>
 ** Copyright (C) 2007 Jason 'vanRijn' Kasper <vR@movingparts.net>
 **
 */

/*
 ** This program is free software; you can redistribute it and/or modify
 ** it under the terms of the GNU Lesser General Public License as published by
 ** the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
 **
 ** You should have received a copy of the GNU Lesser General Public License
 ** along with this program in a file called COPYING; if not, write to
 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 ** MA 02110-1301, USA.
 */

/*
 ** Bug reports and questions can be sent to kde-pim@kde.org
 */

#include "options.h"

#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#include <iostream>

#include <pi-source.h>
#include <pi-socket.h>
#include <pi-dlp.h>
#include <pi-file.h>
#include <pi-buffer.h>

#include <tqdir.h>
#include <tqtimer.h>
#include <tqdatetime.h>
#include <tqthread.h>
#include <tqsocketnotifier.h>

#include <tdeconfig.h>
#include <tdemessagebox.h>
#include <tdestandarddirs.h>
#include <kurl.h>
#include <tdeio/netaccess.h>

#include "pilotUser.h"
#include "pilotSysInfo.h"
#include "pilotCard.h"
#include "pilotSerialDatabase.h"
#include "pilotLocalDatabase.h"

#include "kpilotlink.h"
#include "kpilotdevicelinkPrivate.moc"
#include "kpilotdevicelink.moc"


DeviceMap *DeviceMap::mThis = 0L;


static inline void startOpenTimer(DeviceCommThread *dev, TQTimer *&t)
{
	if ( !t)
	{
		t = new TQTimer(dev);
		TQObject::connect(t, TQ_SIGNAL(timeout()), dev, TQ_SLOT(openDevice()));
	}
	// just a single-shot timer.  we'll know when to start it again...
	t->start(1000, true);
}

DeviceCommThread::DeviceCommThread(KPilotDeviceLink *d) :
	TQThread(),
	fDone(true),
	fHandle(d),
	fOpenTimer(0L),
	fSocketNotifier(0L),
	fSocketNotifierActive(false),
	fWorkaroundUSBTimer(0L),
	fPilotSocket(-1),
	fTempSocket(-1),
	fAcceptedCount(0)
{
	FUNCTIONSETUP;
}


DeviceCommThread::~DeviceCommThread()
{
	FUNCTIONSETUPL(2);
	close();
	KPILOT_DELETE(fWorkaroundUSBTimer);
}

void DeviceCommThread::close()
{
	FUNCTIONSETUPL(2);

	KPILOT_DELETE(fWorkaroundUSBTimer);
	KPILOT_DELETE(fOpenTimer);
	KPILOT_DELETE(fSocketNotifier);
	fSocketNotifierActive=false;

	if (fTempSocket != -1)
	{
		DEBUGKPILOT << fname
		<< ": device comm thread closing socket: ["
		<< fTempSocket << "]" << endl;

		pi_close(fTempSocket);
	}

	if (fPilotSocket != -1)
	{
		DEBUGKPILOT << fname
		<< ": device comm thread closing socket: ["
		<< fPilotSocket << "]" << endl;

		pi_close(fPilotSocket);
	}

	fTempSocket = (-1);
	fPilotSocket = (-1);

	DeviceMap::self()->unbindDevice(link()->fRealPilotPath);
}

void DeviceCommThread::reset()
{
	FUNCTIONSETUP;

	if (link()->fMessages->shouldPrint(Messages::OpenFailMessage))
	{
		TQApplication::postEvent(link(), new DeviceCommEvent(EventLogMessage,
				i18n("Could not open device: %1 (will retry)")
				.arg(link()->pilotPath() )));
	}

	link()->fMessages->reset();
	close();

	// Timer already deleted by close() call.
	startOpenTimer(this,fOpenTimer);

	link()->fLinkStatus = WaitingForDevice;
}

/**
 * This is an asyncronous process.  We try to create a socket with the Palm
 * and then bind to it (in open()).  If we're able to do those 2 things, then
 * we do 2 things:  we set a timeout timer (which will tell us that X amount of
 * time has transpired before we get into the meat of the sync transaction), and
 * we also set up a TQSocketNotifier, which will tell us when data is available
 * to be read from the Palm socket.  If we were unable to create a socket
 * and/or bind to the Palm in this method, we'll start our timer again.
 */
void DeviceCommThread::openDevice()
{
	FUNCTIONSETUPL(2);

	bool deviceOpened = false;

	// This transition (from Waiting to Found) can only be
	// taken once.
	//
	if (link()->fLinkStatus == WaitingForDevice)
	{
		link()->fLinkStatus = FoundDevice;
	}

	if (link()->fMessages->shouldPrint(Messages::OpenMessage))
	{
		TQApplication::postEvent(link(), new DeviceCommEvent(EventLogMessage,
				i18n("Trying to open device %1...")
				.arg(link()->fPilotPath)));
	}

	// if we're not supposed to be done, try to open the main pilot
	// path...
	if (!fDone && link()->fPilotPath.length() > 0)
	{
		DEBUGKPILOT << fname << ": Opening main pilot path: ["
			<< link()->fPilotPath << "]." << endl;
		deviceOpened = open(link()->fPilotPath);
	}

	// only try the temp device if our earlier attempt didn't work and the temp
	// device is different than the main device, and it's a non-empty
	// string
	bool tryTemp = !deviceOpened && (link()->fTempDevice.length() > 0) && (link()->fPilotPath != link()->fTempDevice);

	// if we're not supposed to be done, and we should try the temp
	// device, try the temp device...
	if (!fDone && tryTemp)
	{
		DEBUGKPILOT << fname << ": Couldn't open main pilot path. "
			<< "Now trying temp device: ["
			<< link()->fTempDevice << "]." << endl;
		deviceOpened = open(link()->fTempDevice);
	}

	// if we couldn't connect, try to connect again...
	if (!fDone && !deviceOpened)
	{
		startOpenTimer(this, fOpenTimer);
	}
}

bool DeviceCommThread::open(const TQString &device)
{
	FUNCTIONSETUPL(2);

	int ret;
	int e = 0;
	TQString msg;

	if (fTempSocket != -1)
	{
		pi_close(fTempSocket);
	}
	fTempSocket = (-1);

	link()->fRealPilotPath
			= TDEStandardDirs::realFilePath(device.isEmpty() ? link()->fPilotPath : device);

	if ( !DeviceMap::self()->canBind(link()->fRealPilotPath) )
	{
		msg = i18n("Already listening on that device");

		WARNINGKPILOT << "Pilot Path: ["
			<< link()->fRealPilotPath << "] already connected." << endl;
		WARNINGKPILOT << msg << endl;

		link()->fLinkStatus = PilotLinkError;

		TQApplication::postEvent(link(), new DeviceCommEvent(EventLogError, msg));

		return false;
	}

	DEBUGKPILOT << fname << ": Trying to create socket." << endl;

	fTempSocket = pi_socket(PI_AF_PILOT, PI_SOCK_STREAM, PI_PF_DLP);

	if (fTempSocket < 0)
	{
		e = errno;
		msg = i18n("Cannot create socket for communicating "
				"with the Pilot (%1)").arg(errorMessage(e));
		DEBUGKPILOT << msg << endl;
		DEBUGKPILOT << "(" << strerror(e) << ")" << endl;

		link()->fLinkStatus = PilotLinkError;

		TQApplication::postEvent(link(), new DeviceCommEvent(EventLogError, msg));

		return false;
	}

	DEBUGKPILOT << fname << ": Got socket: [" << fTempSocket << "]" << endl;

	link()->fLinkStatus = CreatedSocket;

	DEBUGKPILOT << fname << ": Binding to path: ["
		<< link()->fRealPilotPath << "]" << endl;

	ret = pi_bind(fTempSocket, TQFile::encodeName(link()->fRealPilotPath));

	if (ret < 0)
	{
		DEBUGKPILOT << fname
			<< ": pi_bind error: ["
			<< strerror(errno) << "]" << endl;

		e = errno;
		msg = i18n("Cannot open Pilot port \"%1\". ").arg(link()->fRealPilotPath);

		DEBUGKPILOT << msg << endl;
		DEBUGKPILOT << "(" << strerror(e) << ")" << endl;

		link()->fLinkStatus = PilotLinkError;

		if (link()->fMessages->shouldPrint(Messages::OpenFailMessage))
		{
			TQApplication::postEvent(link(), new DeviceCommEvent(EventLogError, msg));
		}

		return false;
	}

	link()->fLinkStatus = DeviceOpen;
	DeviceMap::self()->bindDevice(link()->fRealPilotPath);

	fSocketNotifier = new TQSocketNotifier(fTempSocket,
			TQSocketNotifier::Read, this);
	TQObject::connect(fSocketNotifier, TQ_SIGNAL(activated(int)),
			this, TQ_SLOT(acceptDevice()));
	fSocketNotifierActive=true;

	/**
	 * We _always_ want to set a maximum amount of time that we will wait
	 * for the sync process to start.  In the case where our user
	 * has told us that he has a funky USB device, set the workaround timeout
	 * for shorter than normal.
	 */
	int timeout=20000;
	if (link()->fWorkaroundUSB)
	{
		timeout=5000;
	}

	fWorkaroundUSBTimer = new TQTimer(this);
	connect(fWorkaroundUSBTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(workaroundUSB()));
	fWorkaroundUSBTimer->start(timeout, true);

	return true;
}

/**
 * We've been notified by our TQSocketNotifier that we have data available on the
 * socket.  Try to go through the remaining steps of the connnection process.
 * Note: If we return at all from this before the very end without a successful
 * connection, we need to make sure we restart our connection open timer, otherwise
 * it won't be restarted.
 */
void DeviceCommThread::acceptDevice()
{
	FUNCTIONSETUP;

	int ret;

	/**
	 * Our socket notifier should be the only reason that we end up here.
	 * If we're here without him being active, we have a problem.  Try to clean
	 * up and get out.
	 */
	if (!fSocketNotifierActive)
	{
		if (!fAcceptedCount)
		{
		kdWarning() << k_funcinfo << ": Accidentally in acceptDevice()"
			<< endl;
		}
		fAcceptedCount++;
		if (fAcceptedCount>10)
		{
			// Damn the torpedoes
			KPILOT_DELETE(fSocketNotifier);
		}
		return;
	}

	if (fSocketNotifier)
	{
		// fSocketNotifier->setEnabled(false);
		fSocketNotifierActive=false;
		KPILOT_DELETE(fSocketNotifier);
	}

	DEBUGKPILOT << fname << ": Found connection on device: ["
		<< link()->pilotPath().latin1() << "]." <<endl;

	DEBUGKPILOT << fname
		<< ": Current status: ["
		<< link()->statusString()
		<< "] and socket: [" << fTempSocket << "]" << endl;

	ret = pi_listen(fTempSocket, 1);
	if (ret < 0)
	{
		char *s = strerror(errno);

		WARNINGKPILOT << "pi_listen returned: [" << s << "]" << endl;

		// Presumably, strerror() returns things in
		// local8Bit and not latin1.
		TQApplication::postEvent(link(), new DeviceCommEvent(EventLogError,
				i18n("Cannot listen on Pilot socket (%1)").
				arg(TQString::fromLocal8Bit(s))));
		reset();
		return;
	}

	TQApplication::postEvent(link(), new DeviceCommEvent(EventLogProgress, TQString(), 10));

	DEBUGKPILOT << fname <<
		": Listening to pilot. Now trying accept..." << endl;

	int timeout = 20;
	fPilotSocket = pi_accept_to(fTempSocket, 0, 0, timeout);

	if (fPilotSocket < 0)
	{
		char *s = strerror(errno);

		WARNINGKPILOT << "pi_accept returned: [" << s << "]" << endl;

		TQApplication::postEvent(link(), new DeviceCommEvent(EventLogError, i18n("Cannot accept Pilot (%1)")
				.arg(TQString::fromLocal8Bit(s))));

		link()->fLinkStatus = PilotLinkError;
		reset();
		return;
	}

	DEBUGKPILOT << fname << ": Link accept done." << endl;

	if ((link()->fLinkStatus != DeviceOpen) || (fPilotSocket == -1))
	{
		link()->fLinkStatus = PilotLinkError;
		WARNINGKPILOT << "Already connected or unable to connect!" << endl;

		TQApplication::postEvent(link(), new DeviceCommEvent(EventLogError, i18n("Cannot accept Pilot (%1)")
				.arg(i18n("already connected"))));

		reset();
		return;
	}

	TQApplication::postEvent(link(), new DeviceCommEvent(EventLogProgress, TQString(), 30));

	DEBUGKPILOT << fname << ": doing dlp_ReadSysInfo..." << endl;

	struct SysInfo sys_info;
	if (dlp_ReadSysInfo(fPilotSocket, &sys_info) < 0)
	{
		TQApplication::postEvent(link(), new DeviceCommEvent(EventLogError,
				i18n("Unable to read system information from Pilot")));

		link()->fLinkStatus=PilotLinkError;
		reset();
		return;
	}
	else
	{
		DEBUGKPILOT << fname << ": dlp_ReadSysInfo successful..." << endl;

		KPILOT_DELETE(link()->fPilotSysInfo);
		link()->fPilotSysInfo = new KPilotSysInfo(&sys_info);
		DEBUGKPILOT << fname
			<< ": RomVersion: [" << link()->fPilotSysInfo->getRomVersion()
			<< "] Locale: [" << link()->fPilotSysInfo->getLocale()
			<< "] Product: [" << link()->fPilotSysInfo->getProductID()
			<< "]" << endl;
	}

	// If we've made it this far, make sure our USB workaround timer doesn't fire!
	fWorkaroundUSBTimer->stop();
	KPILOT_DELETE(fWorkaroundUSBTimer);

	TQApplication::postEvent(link(), new DeviceCommEvent(EventLogProgress, TQString(), 60));

	KPILOT_DELETE(link()->fPilotUser);
	link()->fPilotUser = new KPilotUser;

	DEBUGKPILOT << fname << ": doing dlp_ReadUserInfo..." << endl;

	/* Ask the pilot who it is.  And see if it's who we think it is. */
	dlp_ReadUserInfo(fPilotSocket, link()->fPilotUser->data());

	TQString n = link()->getPilotUser().name();
	DEBUGKPILOT << fname
		<< ": Read user name: [" << n << "]" << endl;

	TQApplication::postEvent(link(), new DeviceCommEvent(EventLogProgress, i18n("Checking last PC..."), 90));

	/* Tell user (via Pilot) that we are starting things up */
	if ((ret=dlp_OpenConduit(fPilotSocket)) < 0)
	{
		DEBUGKPILOT << fname
			<< ": dlp_OpenConduit returned: [" << ret << "]" << endl;

		TQApplication::postEvent(link(), new DeviceCommEvent(EventLogError,
				i18n("Could not read user information from the Pilot. "
						"Perhaps you have a password set on the device?")));

	}
	link()->fLinkStatus = AcceptedDevice;

	TQApplication::postEvent(link(), new DeviceCommEvent(EventLogProgress, TQString(), 100));

	DeviceCommEvent * ev = new DeviceCommEvent(EventDeviceReady);
	ev->setCurrentSocket(fPilotSocket);
	TQApplication::postEvent(link(), ev);

}

void DeviceCommThread::workaroundUSB()
{
	FUNCTIONSETUP;

	reset();
}

void DeviceCommThread::run()
{
	FUNCTIONSETUP;
	fDone = false;

	startOpenTimer(this, fOpenTimer);

	int sleepBetweenPoll = 2;
	// keep the thread alive until we're supposed to be done
	while (!fDone)
	{
		TQThread::sleep(sleepBetweenPoll);
	}

	close();
	// now sleep one last bit to make sure the pthread inside
	// pilot-link (potentially, if it's libusb) is done before we exit
	TQThread::sleep(1);

	DEBUGKPILOT << fname << ": comm thread now done..." << endl;
}

KPilotDeviceLink::KPilotDeviceLink(TQObject * parent, const char *name,
		const TQString &tempDevice) :
	KPilotLink(parent, name), fLinkStatus(Init), fWorkaroundUSB(false),
			fPilotSocket(-1), fTempDevice(tempDevice), fMessages(new Messages(this)), fDeviceCommThread(0L)
{
	FUNCTIONSETUP;

	DEBUGKPILOT << fname
		<< ": Pilot-link version: [" << PILOT_LINK_NUMBER
		<< "]" << endl;
}

KPilotDeviceLink::~KPilotDeviceLink()
{
	FUNCTIONSETUP;
	close();
	KPILOT_DELETE(fPilotSysInfo);
	KPILOT_DELETE(fPilotUser);
	KPILOT_DELETE(fMessages);
}

/* virtual */bool KPilotDeviceLink::isConnected() const
{
	return fLinkStatus == AcceptedDevice;
}

/* virtual */bool KPilotDeviceLink::event(TQEvent *e)
{
	FUNCTIONSETUP;

	bool handled = false;

	if ((int)e->type() == EventDeviceReady)
	{
		DeviceCommEvent* t = static_cast<DeviceCommEvent*>(e);
		fPilotSocket = t->currentSocket();
		emit deviceReady( this);
		handled = true;
	}
	else if ((int)e->type() == EventLogMessage)
	{
		DeviceCommEvent* t = static_cast<DeviceCommEvent*>(e);
		emit logMessage(t->message());
		handled = true;
	}
	else if ((int)e->type() == EventLogError)
	{
		DeviceCommEvent* t = static_cast<DeviceCommEvent*>(e);
		emit logError(t->message());
		handled = true;
	}
	else if ((int)e->type() == EventLogProgress)
	{
		DeviceCommEvent* t = static_cast<DeviceCommEvent*>(e);
		emit logProgress(t->message(), t->progress());
		handled = true;
	}
	else
	{
		handled = KPilotLink::event(e);
	}

	return handled;
}

void KPilotDeviceLink::stopCommThread()
{
	FUNCTIONSETUP;
	if (fDeviceCommThread)
	{
		fDeviceCommThread->setDone(true);

		// try to wait for our thread to finish, but don't
		// block the main thread forever
		if (fDeviceCommThread->running())
		{
			DEBUGKPILOT << fname
				<< ": comm thread still running. "
				<< "waiting for it to complete." << endl;
			bool done = fDeviceCommThread->wait(5000);
			if (!done)
			{
				DEBUGKPILOT << fname
					<< ": comm thread still running "
					<< "after wait(). "
					<< "going to have to terminate it."
					<< endl;
				// not normally to be done, but we must make sure
				// that this device doesn't come back alive
				fDeviceCommThread->terminate();
				fDeviceCommThread->wait();
			}
		}

		fDeviceCommThread->close();

		KPILOT_DELETE(fDeviceCommThread);
	}
}

void KPilotDeviceLink::close()
{
	FUNCTIONSETUP;

	stopCommThread();

	fPilotSocket = (-1);
}

void KPilotDeviceLink::reset(const TQString & dP)
{
	FUNCTIONSETUP;

	fLinkStatus = Init;

	// Release all resources
	//
	close();
	fPilotPath = TQString();

	fPilotPath = dP;
	if (fPilotPath.isEmpty())
		fPilotPath = fTempDevice;
	if (fPilotPath.isEmpty())
		return;

	reset();
}

void KPilotDeviceLink::startCommThread()
{
	FUNCTIONSETUP;

	stopCommThread();

	if (fTempDevice.isEmpty() && pilotPath().isEmpty())
	{
		WARNINGKPILOT << "No point in trying empty device."
			<< endl;

		TQString msg = i18n("The Pilot device is not configured yet.");
		WARNINGKPILOT << msg << endl;

		fLinkStatus = PilotLinkError;

		emit logError(msg);
		return;
	}

	fDeviceCommThread = new DeviceCommThread(this);
	fDeviceCommThread->start();
}

void KPilotDeviceLink::reset()
{
	FUNCTIONSETUP;

	fMessages->reset();
	close();

	checkDevice();

	fLinkStatus = WaitingForDevice;

	startCommThread();
}

void KPilotDeviceLink::checkDevice()
{
	// If the device exists yet doesn't have the right
	// permissions, complain and then continue anyway.
	//
	TQFileInfo fi(fPilotPath);
	if (fi.exists())
	{
		// If it exists, it ought to be RW already.
		//
		if (!(fi.isReadable() && fi.isWritable()))
		{
			emit logError(i18n("Pilot device %1 is not read-write.")
			.arg(fPilotPath));
		}
	}
	else
	{
		// It doesn't exist, mention this in the log
		// (relevant as long as we use only one device type)
		//
		emit
				logError(i18n("Pilot device %1 does not exist. "
						"Probably it is a USB device and will appear during a HotSync.")
				.arg(fPilotPath));
		// Suppress all normal and error messages about opening the device.
		fMessages->block(Messages::OpenMessage | Messages::OpenFailMessage,
				true);
	}
}

void KPilotDeviceLink::setTempDevice(const TQString &d)
{
	fTempDevice = d;
	DeviceMap::self()->bindDevice(fTempDevice);
}

/* virtual */bool KPilotDeviceLink::tickle()
{
	// No FUNCTIONSETUP here because it may be called from
	// a separate thread.
	return pi_tickle(pilotSocket()) >= 0;
}

/* virtual */void KPilotDeviceLink::addSyncLogEntryImpl(const TQString &entry)
{
	dlp_AddSyncLogEntry(fPilotSocket,
			const_cast<char *>((const char *)Pilot::toPilot(entry)));
}

bool KPilotDeviceLink::installFile(const TQString & f, const bool deleteFile)
{
	FUNCTIONSETUP;

	DEBUGKPILOT << fname << ": Installing file " << f << endl;

	if (!TQFile::exists(f))
		return false;

	char buffer[PATH_MAX];
	memset(buffer, 0, PATH_MAX);
	strlcpy(buffer, TQFile::encodeName(f), PATH_MAX);
	struct pi_file *pf = pi_file_open(buffer);

	if (!f)
	{
		WARNINGKPILOT << "Cannot open file " << f << endl;
		emit logError(i18n
			("<qt>Cannot install the file &quot;%1&quot;.</qt>").
		arg(f));
		return false;
	}

	if (pi_file_install(pf, fPilotSocket, 0, 0L) < 0)
	{
		WARNINGKPILOT << "Cannot pi_file_install " << f << endl;
		emit logError(i18n
				("<qt>Cannot install the file &quot;%1&quot;.</qt>").
		arg(f));
		return false;
	}

	pi_file_close(pf);
	if (deleteFile)
		TQFile::remove(f);

	return true;
}

int KPilotDeviceLink::openConduit()
{
	return dlp_OpenConduit(fPilotSocket);
}

TQString KPilotDeviceLink::statusString(LinkStatus l)
{
	TQString s= CSL1("KPilotDeviceLink=");

	switch (l)
	{
	case Init:
		s.append(CSL1("Init"));
		break;
	case WaitingForDevice:
		s.append(CSL1("WaitingForDevice"));
		break;
	case FoundDevice:
		s.append(CSL1("FoundDevice"));
		break;
	case CreatedSocket:
		s.append(CSL1("CreatedSocket"));
		break;
	case DeviceOpen:
		s.append(CSL1("DeviceOpen"));
		break;
	case AcceptedDevice:
		s.append(CSL1("AcceptedDevice"));
		break;
	case SyncDone:
		s.append(CSL1("SyncDone"));
		break;
	case PilotLinkError:
		s.append(CSL1("PilotLinkError"));
		break;
	case WorkaroundUSB:
		s.append(CSL1("WorkaroundUSB"));
		break;
	}

	return s;
}

TQString KPilotDeviceLink::statusString() const
{
	return statusString(status() );
}

void KPilotDeviceLink::endSync(EndOfSyncFlags f)
{
	FUNCTIONSETUP;

	if (UpdateUserInfo == f)
	{
		getPilotUser().setLastSyncPC((unsigned long) gethostid());
		getPilotUser().setLastSyncDate(time(0));

		DEBUGKPILOT << fname << ": Writing username " << getPilotUser().name() << endl;

		dlp_WriteUserInfo(pilotSocket(), getPilotUser().data());
		addSyncLogEntry(i18n("End of HotSync\n"));
	}
	dlp_EndOfSync(pilotSocket(), 0);
	KPILOT_DELETE(fPilotSysInfo);
	KPILOT_DELETE(fPilotUser);
}

int KPilotDeviceLink::getNextDatabase(int index, struct DBInfo *dbinfo)
{
	FUNCTIONSETUP;

	pi_buffer_t buf = 	{ 0, 0, 0 };
	int r = dlp_ReadDBList(pilotSocket(), 0, dlpDBListRAM, index, &buf);
	if (r >= 0)
	{
		memcpy(dbinfo, buf.data, sizeof(struct DBInfo));
	}
	return r;
}

// Find a database with the given name. Info about the DB is stored into dbinfo (e.g. to be used later on with retrieveDatabase).
int KPilotDeviceLink::findDatabase(const char *name, struct DBInfo *dbinfo,
		int index, unsigned long type, unsigned long creator)
{
	FUNCTIONSETUP;
	return dlp_FindDBInfo(pilotSocket(), 0, index, const_cast<char *>(name),
			type, creator, dbinfo);
}

bool KPilotDeviceLink::retrieveDatabase(const TQString &fullBackupName,
		DBInfo *info)
{
	FUNCTIONSETUP;

	if (fullBackupName.isEmpty() || !info)
	{
		// Don't even bother trying to convert or retrieve.
		return false;
	}

	DEBUGKPILOT << fname << ": Writing DB <" << info->name << "> "
		<< " to " << fullBackupName << endl;

	TQCString encodedName = TQFile::encodeName(fullBackupName);
	struct pi_file *f = pi_file_create(encodedName, info);

	if (!f)
	{
		WARNINGKPILOT << "Failed, unable to create file" << endl;
		return false;
	}

	if (pi_file_retrieve(f, pilotSocket(), 0, 0L) < 0)
	{
		WARNINGKPILOT << "Failed, unable to back up database" << endl;

		pi_file_close(f);
		return false;
	}

	pi_file_close(f);
	return true;
}

KPilotLink::DBInfoList KPilotDeviceLink::getDBList(int cardno, int flags)
{
	bool cont=true;
	DBInfoList dbs;
	int index=0;
	while (cont)
	{
		pi_buffer_t buf = { 0, 0, 0 };
		pi_buffer_clear(&buf);
		// DBInfo*dbi=new DBInfo();
		if (dlp_ReadDBList(pilotSocket(), cardno, flags | dlpDBListMultiple,
				index, &buf)<0)
		{
			cont=false;
		}
		else
		{
			DBInfo db_n;
			DBInfo *db_it = (DBInfo *)buf.data;
			int info_count = buf.used / sizeof(struct DBInfo);

			while (info_count>0)
			{
				memcpy(&db_n, db_it, sizeof(struct DBInfo));
				++db_it;
				info_count--;
				dbs.append(db_n);
			}
			index=db_n.index+1;
		}
	}
	return dbs;
}

const KPilotCard *KPilotDeviceLink::getCardInfo(int card)
{
	KPilotCard *cardinfo=new KPilotCard();
	if (dlp_ReadStorageInfo(pilotSocket(), card, cardinfo->cardInfo())<0)
	{
		WARNINGKPILOT << "Could not get info for card " << card << endl;

		KPILOT_DELETE(cardinfo);
		return 0L;
	}
	return cardinfo;
}

PilotDatabase *KPilotDeviceLink::database(const TQString &name)
{
	return new PilotSerialDatabase( this, name );
}

PilotDatabase *KPilotDeviceLink::database(const DBInfo *info)
{
	return new PilotSerialDatabase( this, info );
}

