/*
 * tqca.cpp - TQt Cryptographic Architecture
 * Copyright (C) 2003  Justin Karneges
 * Copyright (C) 2010-2020  TDE Team
 *
 * This library 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 library 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include "tqca.h"
#include "tqcaprovider.h"

#include <tqptrlist.h>
#include <tqdir.h>
#include <tqfileinfo.h>
#include <tqstringlist.h>
#include <tqlibrary.h>
#include <tqtimer.h>
#include <tqhostaddress.h>
#include <tqapplication.h>
#include <tqguardedptr.h>
#include <cstdlib>

#if defined(Q_OS_WIN32)
#define PLUGIN_EXT "dll"
#elif defined(Q_OS_MAC)
#define PLUGIN_EXT "dylib"
#else
#define PLUGIN_EXT "so"
#endif

using namespace TQCA;

class ProviderItem
{
public:
	TQCAProvider *p;
	TQString fname;

	static ProviderItem *load(const TQString &fname)
	{
		TQLibrary *lib = new TQLibrary(fname);
		if(!lib->load()) {
			delete lib;
			return 0;
		}
		void *s = lib->resolve("createProvider");
		if(!s) {
			delete lib;
			return 0;
		}
		TQCAProvider *(*createProvider)() = (TQCAProvider *(*)())s;
		TQCAProvider *p = createProvider();
		if(!p) {
			delete lib;
			return 0;
		}
		ProviderItem *i = new ProviderItem(lib, p);
		i->fname = fname;
		return i;
	}

	static ProviderItem *fromClass(TQCAProvider *p)
	{
		ProviderItem *i = new ProviderItem(0, p);
		return i;
	}

	~ProviderItem()
	{
		delete p;
		delete lib;
	}

	void ensureInit()
	{
		if(init_done)
			return;
		init_done = true;
		p->init();
	}

private:
	TQLibrary *lib;
	bool init_done;

	ProviderItem(TQLibrary *_lib, TQCAProvider *_p)
	{
		lib = _lib;
		p = _p;
		init_done = false;
	}
};

static TQPtrList<ProviderItem> providerList;
static bool qca_init = false;

static bool plugin_have(const TQString &fname)
{
	TQPtrListIterator<ProviderItem> it(providerList);
	for(ProviderItem *i; (i = it.current()); ++it) {
		if(i->fname == fname)
			return true;
	}
	return false;
}

static void plugin_scan()
{
	TQStringList dirs = TQApplication::libraryPaths();
	for(TQStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) {
		TQDir libpath(*it);
		TQDir dir(libpath.filePath("crypto"));
		if(!dir.exists())
			continue;

		TQStringList list = dir.entryList();
		for(TQStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
			TQFileInfo fi(dir.filePath(*it));
			if(fi.isDir())
				continue;
			if(fi.extension() != PLUGIN_EXT)
				continue;
			TQString fname = fi.filePath();

			// don't load the same plugin again!
			if(plugin_have(fname))
				continue;
			//printf("f=[%s]\n", fname.latin1());

			ProviderItem *i = ProviderItem::load(fname);
			if(!i)
				continue;
			if(i->p->qcaVersion() != TQCA_PLUGIN_VERSION) {
				delete i;
				continue;
			}

			providerList.append(i);
		}
	}
}

static void plugin_addClass(TQCAProvider *p)
{
	ProviderItem *i = ProviderItem::fromClass(p);
	providerList.prepend(i);
}

static void plugin_unloadall()
{
	providerList.clear();
}

static int plugin_caps()
{
	int caps = 0;
	TQPtrListIterator<ProviderItem> it(providerList);
	for(ProviderItem *i; (i = it.current()); ++it)
		caps |= i->p->capabilities();
	return caps;
}

TQString TQCA::arrayToHex(const TQByteArray &a)
{
	TQString out;
	for(int n = 0; n < (int)a.size(); ++n) {
		TQString str;
		str.sprintf("%02x", (uchar)a[n]);
		out.append(str);
	}
	return out;
}

TQByteArray TQCA::hexToArray(const TQString &str)
{
	TQByteArray out(str.length() / 2);
	int at = 0;
	for(int n = 0; n + 1 < (int)str.length(); n += 2) {
		uchar a = str[n];
		uchar b = str[n+1];
		uchar c = ((a & 0x0f) << 4) + (b & 0x0f);
		out[at++] = c;
	}
	return out;
}

void TQCA::init()
{
	if(qca_init)
		return;
	qca_init = true;
	providerList.setAutoDelete(true);
}

bool TQCA::isSupported(int capabilities)
{
	init();

	int caps = plugin_caps();
	if(caps & capabilities)
		return true;

	// ok, try scanning for new stuff
	plugin_scan();
	caps = plugin_caps();
	if(caps & capabilities)
		return true;

	return false;
}

void TQCA::insertProvider(TQCAProvider *p)
{
	plugin_addClass(p);
}

void TQCA::unloadAllPlugins()
{
	plugin_unloadall();
}

static void *getContext(int cap)
{
	init();

	// this call will also trip a scan for new plugins if needed
	if(!TQCA::isSupported(cap))
		return 0;

	TQPtrListIterator<ProviderItem> it(providerList);
	for(ProviderItem *i; (i = it.current()); ++it) {
		if(i->p->capabilities() & cap) {
			i->ensureInit();
			return i->p->context(cap);
		}
	}
	return 0;
}


//----------------------------------------------------------------------------
// Hash
//----------------------------------------------------------------------------
class Hash::Private
{
public:
	Private()
	{
		c = 0;
	}

	~Private()
	{
		delete c;
	}

	void reset()
	{
		c->reset();
	}

	TQCA_HashContext *c;
};

Hash::Hash(TQCA_HashContext *c)
{
	d = new Private;
	d->c = c;
}

Hash::Hash(const Hash &from)
{
	d = new Private;
	*this = from;
}

Hash & Hash::operator=(const Hash &from)
{
	delete d->c;
	d->c = from.d->c->clone();
	return *this;
}

Hash::~Hash()
{
	delete d;
}

void Hash::clear()
{
	d->reset();
}

void Hash::update(const TQByteArray &a)
{
	d->c->update(a.data(), a.size());
}

TQByteArray Hash::final()
{
	TQByteArray buf;
	d->c->final(&buf);
	return buf;
}


//----------------------------------------------------------------------------
// Cipher
//----------------------------------------------------------------------------
class Cipher::Private
{
public:
	Private()
	{
		c = 0;
	}

	~Private()
	{
		delete c;
	}

	void reset()
	{
		dir = Encrypt;
		key.resize(0);
		iv.resize(0);
		err = false;
	}

	TQCA_CipherContext *c;
	int dir;
	int mode;
	TQByteArray key, iv;
	bool err;
};

Cipher::Cipher(TQCA_CipherContext *c, int dir, int mode, const TQByteArray &key, const TQByteArray &iv, bool pad)
{
	d = new Private;
	d->c = c;
	reset(dir, mode, key, iv, pad);
}

Cipher::Cipher(const Cipher &from)
{
	d = new Private;
	*this = from;
}

Cipher & Cipher::operator=(const Cipher &from)
{
	delete d->c;
	d->c = from.d->c->clone();
	d->dir = from.d->dir;
	d->mode = from.d->mode;
	d->key = from.d->key.copy();
	d->iv = from.d->iv.copy();
	d->err = from.d->err;
	return *this;
}

Cipher::~Cipher()
{
	delete d;
}

TQByteArray Cipher::dyn_generateKey(int size) const
{
	TQByteArray buf;
	if(size != -1)
		buf.resize(size);
	else
		buf.resize(d->c->keySize());
	if(!d->c->generateKey(buf.data(), size))
		return TQByteArray();
	return buf;
}

TQByteArray Cipher::dyn_generateIV() const
{
	TQByteArray buf(d->c->blockSize());
	if(!d->c->generateIV(buf.data()))
		return TQByteArray();
	return buf;
}

void Cipher::reset(int dir, int mode, const TQByteArray &key, const TQByteArray &iv, bool pad)
{
	d->reset();

	d->dir = dir;
	d->mode = mode;
	d->key = key.copy();
	d->iv = iv.copy();
	if(!d->c->setup(d->dir, d->mode, d->key.isEmpty() ? 0: d->key.data(), d->key.size(), d->iv.isEmpty() ? 0 : d->iv.data(), pad)) {
		d->err = true;
		return;
	}
}

bool Cipher::update(const TQByteArray &a)
{
	if(d->err)
		return false;

	if(!a.isEmpty()) {
		if(!d->c->update(a.data(), a.size())) {
			d->err = true;
			return false;
		}
	}
	return true;
}

TQByteArray Cipher::final(bool *ok)
{
	if(ok)
		*ok = false;
	if(d->err)
		return TQByteArray();

	TQByteArray out;
	if(!d->c->final(&out)) {
		d->err = true;
		return TQByteArray();
	}
	if(ok)
		*ok = true;
	return out;
}


//----------------------------------------------------------------------------
// SHA1
//----------------------------------------------------------------------------
SHA1::SHA1()
:Hash((TQCA_HashContext *)getContext(CAP_SHA1))
{
}


//----------------------------------------------------------------------------
// SHA256
//----------------------------------------------------------------------------
SHA256::SHA256()
:Hash((TQCA_HashContext *)getContext(CAP_SHA256))
{
}


//----------------------------------------------------------------------------
// MD5
//----------------------------------------------------------------------------
MD5::MD5()
:Hash((TQCA_HashContext *)getContext(CAP_MD5))
{
}


//----------------------------------------------------------------------------
// BlowFish
//----------------------------------------------------------------------------
BlowFish::BlowFish(int dir, int mode, const TQByteArray &key, const TQByteArray &iv, bool pad)
:Cipher((TQCA_CipherContext *)getContext(CAP_BlowFish), dir, mode, key, iv, pad)
{
}


//----------------------------------------------------------------------------
// TripleDES
//----------------------------------------------------------------------------
TripleDES::TripleDES(int dir, int mode, const TQByteArray &key, const TQByteArray &iv, bool pad)
:Cipher((TQCA_CipherContext *)getContext(CAP_TripleDES), dir, mode, key, iv, pad)
{
}


//----------------------------------------------------------------------------
// AES128
//----------------------------------------------------------------------------
AES128::AES128(int dir, int mode, const TQByteArray &key, const TQByteArray &iv, bool pad)
:Cipher((TQCA_CipherContext *)getContext(CAP_AES128), dir, mode, key, iv, pad)
{
}


//----------------------------------------------------------------------------
// AES256
//----------------------------------------------------------------------------
AES256::AES256(int dir, int mode, const TQByteArray &key, const TQByteArray &iv, bool pad)
:Cipher((TQCA_CipherContext *)getContext(CAP_AES256), dir, mode, key, iv, pad)
{
}


//----------------------------------------------------------------------------
// RSAKey
//----------------------------------------------------------------------------
class RSAKey::Private
{
public:
	Private()
	{
		c = 0;
	}

	~Private()
	{
		delete c;
	}

	TQCA_RSAKeyContext *c;
};

RSAKey::RSAKey()
{
	d = new Private;
	d->c = (TQCA_RSAKeyContext *)getContext(CAP_RSA);
}

RSAKey::RSAKey(const RSAKey &from)
{
	d = new Private;
	*this = from;
}

RSAKey & RSAKey::operator=(const RSAKey &from)
{
	delete d->c;
	d->c = from.d->c->clone();
	return *this;
}

RSAKey::~RSAKey()
{
	delete d;
}

bool RSAKey::isNull() const
{
	return d->c->isNull();
}

bool RSAKey::havePublic() const
{
	return d->c->havePublic();
}

bool RSAKey::havePrivate() const
{
	return d->c->havePrivate();
}

TQByteArray RSAKey::toDER(bool publicOnly) const
{
	TQByteArray out;
	if(!d->c->toDER(&out, publicOnly))
		return TQByteArray();
	return out;
}

bool RSAKey::fromDER(const TQByteArray &a)
{
	return d->c->createFromDER(a.data(), a.size());
}

TQString RSAKey::toPEM(bool publicOnly) const
{
	TQByteArray out;
	if(!d->c->toPEM(&out, publicOnly))
		return TQByteArray();

	TQCString cs;
	cs.resize(out.size()+1);
	memcpy(cs.data(), out.data(), out.size());
	return TQString::fromLatin1(cs);
}

bool RSAKey::fromPEM(const TQString &str)
{
	TQCString cs = str.latin1();
	TQByteArray a(cs.length());
	memcpy(a.data(), cs.data(), a.size());
	return d->c->createFromPEM(a.data(), a.size());
}

bool RSAKey::fromNative(void *p)
{
	return d->c->createFromNative(p);
}

bool RSAKey::encrypt(const TQByteArray &a, TQByteArray *b, bool oaep) const
{
	TQByteArray out;
	if(!d->c->encrypt(a, &out, oaep))
		return false;
	*b = out;
	return true;
}

bool RSAKey::decrypt(const TQByteArray &a, TQByteArray *b, bool oaep) const
{
	TQByteArray out;
	if(!d->c->decrypt(a, &out, oaep))
		return false;
	*b = out;
	return true;
}

bool RSAKey::generate(unsigned int bits)
{
	return d->c->generate(bits);
}


//----------------------------------------------------------------------------
// RSA
//----------------------------------------------------------------------------
RSA::RSA()
{
}

RSA::~RSA()
{
}

RSAKey RSA::key() const
{
	return v_key;
}

void RSA::setKey(const RSAKey &k)
{
	v_key = k;
}

bool RSA::encrypt(const TQByteArray &a, TQByteArray *b, bool oaep) const
{
	if(v_key.isNull())
		return false;
	return v_key.encrypt(a, b, oaep);
}

bool RSA::decrypt(const TQByteArray &a, TQByteArray *b, bool oaep) const
{
	if(v_key.isNull())
		return false;
	return v_key.decrypt(a, b, oaep);
}

RSAKey RSA::generateKey(unsigned int bits)
{
	RSAKey k;
	k.generate(bits);
	return k;
}


//----------------------------------------------------------------------------
// Cert
//----------------------------------------------------------------------------
class Cert::Private
{
public:
	Private()
	{
		c = 0;
	}

	~Private()
	{
		delete c;
	}

	TQCA_CertContext *c;
};

Cert::Cert()
{
	d = new Private;
	// crash because this is returning 0
	d->c = (TQCA_CertContext *)getContext(CAP_X509);
}

Cert::Cert(const Cert &from)
{
	d = new Private;
	*this = from;
}

Cert & Cert::operator=(const Cert &from)
{
	delete d->c;
	if ( from.d->c )
		d->c = from.d->c->clone();
	else
		d->c = 0;
	return *this;
}

Cert::~Cert()
{
	delete d;
}

void Cert::fromContext(TQCA_CertContext *ctx)
{
	delete d->c;
	d->c = ctx;
}

bool Cert::isNull() const
{
	return d->c->isNull();
}

TQString Cert::commonName() const
{
	CertProperties props = subject();
	return props["CN"];
}

TQString Cert::serialNumber() const
{
	return d->c->serialNumber();
}

TQString Cert::subjectString() const
{
	return d->c->subjectString();
}

TQString Cert::issuerString() const
{
	return d->c->issuerString();
}

CertProperties Cert::subject() const
{
	TQValueList<TQCA_CertProperty> list = d->c->subject();
	CertProperties props;
	for(TQValueList<TQCA_CertProperty>::ConstIterator it = list.begin(); it != list.end(); ++it)
		props[(*it).var] = (*it).val;
	return props;
}

CertProperties Cert::issuer() const
{
	TQValueList<TQCA_CertProperty> list = d->c->issuer();
	CertProperties props;
	for(TQValueList<TQCA_CertProperty>::ConstIterator it = list.begin(); it != list.end(); ++it)
		props[(*it).var] = (*it).val;
	return props;
}

TQDateTime Cert::notBefore() const
{
	return d->c->notBefore();
}

TQDateTime Cert::notAfter() const
{
	return d->c->notAfter();
}

TQByteArray Cert::toDER() const
{
	TQByteArray out;
	if(!d->c->toDER(&out))
		return TQByteArray();
	return out;
}

bool Cert::fromDER(const TQByteArray &a)
{
	return d->c->createFromDER(a.data(), a.size());
}

TQString Cert::toPEM() const
{
	TQByteArray out;
	if(!d->c->toPEM(&out))
		return TQByteArray();

	TQCString cs;
	cs.resize(out.size()+1);
	memcpy(cs.data(), out.data(), out.size());
	return TQString::fromLatin1(cs);
}

bool Cert::fromPEM(const TQString &str)
{
	TQCString cs = str.latin1();
	TQByteArray a(cs.length());
	memcpy(a.data(), cs.data(), a.size());
	return d->c->createFromPEM(a.data(), a.size());
}


//----------------------------------------------------------------------------
// TLS
//----------------------------------------------------------------------------
class TLS::Private
{
public:
	Private()
	{
		c = (TQCA_TLSContext *)getContext(CAP_TLS);
	}

	~Private()
	{
		delete c;
	}

	void reset()
	{
		handshaken = false;
		closing = false;
		in.resize(0);
		out.resize(0);
		from_net.resize(0);
		to_net.resize(0);
		host = "";
		hostMismatch = false;
		// this causes the crash, because the Cert ctor is setting a null context
		cert = Cert();
		bytesEncoded = 0;
		tryMore = false;
	}

	void appendArray(TQByteArray *a, const TQByteArray &b)
	{
		int oldsize = a->size();
		a->resize(oldsize + b.size());
		memcpy(a->data() + oldsize, b.data(), b.size());
	}

	Cert cert;
	TQCA_TLSContext *c;
	TQByteArray in, out, to_net, from_net;
	int bytesEncoded;
	bool tryMore;
	bool handshaken;
	TQString host;
	bool hostMismatch;
	bool closing;

	Cert ourCert;
	RSAKey ourKey;
	TQPtrList<TQCA_CertContext> store;
};

TLS::TLS(TQObject *parent)
:TQObject(parent)
{
	d = new Private;
}

TLS::~TLS()
{
	delete d;
}

void TLS::setCertificate(const Cert &cert, const RSAKey &key)
{
	d->ourCert = cert;
	d->ourKey = key;
}

void TLS::setCertificateStore(const TQPtrList<Cert> &store)
{
	// convert the cert list into a context list
	d->store.clear();
	TQPtrListIterator<Cert> it(store);
	for(Cert *cert; (cert = it.current()); ++it)
		d->store.append(cert->d->c);
}

void TLS::reset()
{
	d->reset();
}

bool TLS::startClient(const TQString &host)
{
	d->reset();
	d->host = host;

	if(!d->c->startClient(d->store, *d->ourCert.d->c, *d->ourKey.d->c))
		return false;
	TQTimer::singleShot(0, this, TQ_SLOT(update()));
	return true;
}

bool TLS::startServer()
{
	d->reset();

	if(!d->c->startServer(d->store, *d->ourCert.d->c, *d->ourKey.d->c))
		return false;
	TQTimer::singleShot(0, this, TQ_SLOT(update()));
	return true;
}

void TLS::close()
{
	if(!d->handshaken || d->closing)
		return;

	d->closing = true;
	TQTimer::singleShot(0, this, TQ_SLOT(update()));
}

bool TLS::isHandshaken() const
{
	return d->handshaken;
}

void TLS::write(const TQByteArray &a)
{
	d->appendArray(&d->out, a);
	update();
}

TQByteArray TLS::read()
{
	TQByteArray a = d->in.copy();
	d->in.resize(0);
	return a;
}

void TLS::writeIncoming(const TQByteArray &a)
{
	d->appendArray(&d->from_net, a);
	update();
}

TQByteArray TLS::readOutgoing()
{
	TQByteArray a = d->to_net.copy();
	d->to_net.resize(0);
	return a;
}

TQByteArray TLS::readUnprocessed()
{
	TQByteArray a = d->from_net.copy();
	d->from_net.resize(0);
	return a;
}

const Cert & TLS::peerCertificate() const
{
	return d->cert;
}

int TLS::certificateValidityResult() const
{
	if(d->hostMismatch)
		return TQCA::TLS::HostMismatch;
	else
		return d->c->validityResult();
}

void TLS::update()
{
	bool force_read = false;
	bool eof = false;
	bool done = false;
	TQGuardedPtr<TLS> self = this;

	if(d->closing) {
		TQByteArray a;
		int r = d->c->shutdown(d->from_net, &a);
		d->from_net.resize(0);
		if(r == TQCA_TLSContext::Error) {
			reset();
			error(ErrHandshake);
			return;
		}
		if(r == TQCA_TLSContext::Success) {
			d->from_net = d->c->unprocessed().copy();
			done = true;
		}
		d->appendArray(&d->to_net, a);
	}
	else {
		if(!d->handshaken) {
			TQByteArray a;
			int r = d->c->handshake(d->from_net, &a);
			d->from_net.resize(0);
			if(r == TQCA_TLSContext::Error) {
				reset();
				error(ErrHandshake);
				return;
			}
			d->appendArray(&d->to_net, a);
			if(r == TQCA_TLSContext::Success) {
				TQCA_CertContext *cc = d->c->peerCertificate();
				if(cc && !d->host.isEmpty() && d->c->validityResult() == TQCA::TLS::Valid) {
					if(!cc->matchesAddress(d->host))
						d->hostMismatch = true;
				}
				d->cert.fromContext(cc);
				d->handshaken = true;
				handshaken();
				if(!self)
					return;

				// there is a teeny tiny possibility that incoming data awaits.  let us get it.
				force_read = true;
			}
		}

		if(d->handshaken) {
			if(!d->out.isEmpty() || d->tryMore) {
				d->tryMore = false;
				TQByteArray a;
				int enc;
				bool more = false;
				bool ok = d->c->encode(d->out, &a, &enc);
				eof = d->c->eof();
				if(ok && enc < (int)d->out.size())
					more = true;
				d->out.resize(0);
				if(!eof) {
					if(!ok) {
						reset();
						error(ErrCrypt);
						return;
					}
					d->bytesEncoded += enc;
					if(more)
						d->tryMore = true;
					d->appendArray(&d->to_net, a);
				}
			}
			if(!d->from_net.isEmpty() || force_read) {
				TQByteArray a, b;
				bool ok = d->c->decode(d->from_net, &a, &b);
				eof = d->c->eof();
				d->from_net.resize(0);
				if(!ok) {
					reset();
					error(ErrCrypt);
					return;
				}
				d->appendArray(&d->in, a);
				d->appendArray(&d->to_net, b);
			}

			if(!d->in.isEmpty()) {
				readyRead();
				if(!self)
					return;
			}
		}
	}

	if(!d->to_net.isEmpty()) {
		int bytes = d->bytesEncoded;
		d->bytesEncoded = 0;
		readyReadOutgoing(bytes);
		if(!self)
			return;
	}

	if(eof) {
		close();
		if(!self)
			return;
		return;
	}

	if(d->closing && done) {
		reset();
		closed();
	}
}


//----------------------------------------------------------------------------
// SASL
//----------------------------------------------------------------------------
TQString saslappname = "qca";
class SASL::Private
{
public:
	Private()
	{
		c = (TQCA_SASLContext *)getContext(CAP_SASL);
	}

	~Private()
	{
		delete c;
	}

	void setSecurityProps()
	{
		c->setSecurityProps(noPlain, noActive, noDict, noAnon, reqForward, reqCreds, reqMutual, ssfmin, ssfmax, ext_authid, ext_ssf);
	}

	// security opts
	bool noPlain, noActive, noDict, noAnon, reqForward, reqCreds, reqMutual;
	int ssfmin, ssfmax;
	TQString ext_authid;
	int ext_ssf;

	bool tried;
	TQCA_SASLContext *c;
	TQHostAddress localAddr, remoteAddr;
	int localPort, remotePort;
	TQByteArray stepData;
	bool allowCSF;
	bool first, server;

	TQByteArray inbuf, outbuf;
};

SASL::SASL(TQObject *parent)
:TQObject(parent)
{
	d = new Private;
	reset();
}

SASL::~SASL()
{
	delete d;
}

void SASL::setAppName(const TQString &name)
{
	saslappname = name;
}

void SASL::reset()
{
	d->localPort = -1;
	d->remotePort = -1;

	d->noPlain = false;
	d->noActive = false;
	d->noDict = false;
	d->noAnon = false;
	d->reqForward = false;
	d->reqCreds = false;
	d->reqMutual = false;
	d->ssfmin = 0;
	d->ssfmax = 0;
	d->ext_authid = "";
	d->ext_ssf = 0;

	d->inbuf.resize(0);
	d->outbuf.resize(0);

	d->c->reset();
}

int SASL::errorCondition() const
{
	return d->c->errorCond();
}

void SASL::setAllowPlain(bool b)
{
	d->noPlain = !b;
}

void SASL::setAllowAnonymous(bool b)
{
	d->noAnon = !b;
}

void SASL::setAllowActiveVulnerable(bool b)
{
	d->noActive = !b;
}

void SASL::setAllowDictionaryVulnerable(bool b)
{
	d->noDict = !b;
}

void SASL::setRequireForwardSecrecy(bool b)
{
	d->reqForward = b;
}

void SASL::setRequirePassCredentials(bool b)
{
	d->reqCreds = b;
}

void SASL::setRequireMutualAuth(bool b)
{
	d->reqMutual = b;
}

void SASL::setMinimumSSF(int x)
{
	d->ssfmin = x;
}

void SASL::setMaximumSSF(int x)
{
	d->ssfmax = x;
}

void SASL::setExternalAuthID(const TQString &authid)
{
	d->ext_authid = authid;
}

void SASL::setExternalSSF(int x)
{
	d->ext_ssf = x;
}

void SASL::setLocalAddr(const TQHostAddress &addr, TQ_UINT16 port)
{
	d->localAddr = addr;
	d->localPort = port;
}

void SASL::setRemoteAddr(const TQHostAddress &addr, TQ_UINT16 port)
{
	d->remoteAddr = addr;
	d->remotePort = port;
}

bool SASL::startClient(const TQString &service, const TQString &host, const TQStringList &mechlist, bool allowClientSendFirst)
{
	TQCA_SASLHostPort la, ra;
	if(d->localPort != -1) {
		la.addr = d->localAddr;
		la.port = d->localPort;
	}
	if(d->remotePort != -1) {
		ra.addr = d->remoteAddr;
		ra.port = d->remotePort;
	}

	d->allowCSF = allowClientSendFirst;
	d->c->setCoreProps(service, host, d->localPort != -1 ? &la : 0, d->remotePort != -1 ? &ra : 0);
	d->setSecurityProps();

	if(!d->c->clientStart(mechlist))
		return false;
	d->first = true;
	d->server = false;
	d->tried = false;
	TQTimer::singleShot(0, this, TQ_SLOT(tryAgain()));
	return true;
}

bool SASL::startServer(const TQString &service, const TQString &host, const TQString &realm, TQStringList *mechlist)
{
	TQCA_SASLHostPort la, ra;
	if(d->localPort != -1) {
		la.addr = d->localAddr;
		la.port = d->localPort;
	}
	if(d->remotePort != -1) {
		ra.addr = d->remoteAddr;
		ra.port = d->remotePort;
	}

	d->c->setCoreProps(service, host, d->localPort != -1 ? &la : 0, d->remotePort != -1 ? &ra : 0);
	d->setSecurityProps();

	if(!d->c->serverStart(realm, mechlist, saslappname))
		return false;
	d->first = true;
	d->server = true;
	d->tried = false;
	return true;
}

void SASL::putServerFirstStep(const TQString &mech)
{
	int r = d->c->serverFirstStep(mech, 0);
	handleServerFirstStep(r);
}

void SASL::putServerFirstStep(const TQString &mech, const TQByteArray &clientInit)
{
	int r = d->c->serverFirstStep(mech, &clientInit);
	handleServerFirstStep(r);
}

void SASL::handleServerFirstStep(int r)
{
	if(r == TQCA_SASLContext::Success)
		authenticated();
	else if(r == TQCA_SASLContext::Continue)
		nextStep(d->c->result());
	else if(r == TQCA_SASLContext::AuthCheck)
		tryAgain();
	else
		error(ErrAuth);
}

void SASL::putStep(const TQByteArray &stepData)
{
	d->stepData = stepData.copy();
	tryAgain();
}

void SASL::setUsername(const TQString &user)
{
	d->c->setClientParams(&user, 0, 0, 0);
}

void SASL::setAuthzid(const TQString &authzid)
{
	d->c->setClientParams(0, &authzid, 0, 0);
}

void SASL::setPassword(const TQString &pass)
{
	d->c->setClientParams(0, 0, &pass, 0);
}

void SASL::setRealm(const TQString &realm)
{
	d->c->setClientParams(0, 0, 0, &realm);
}

void SASL::continueAfterParams()
{
	tryAgain();
}

void SASL::continueAfterAuthCheck()
{
	tryAgain();
}

void SASL::tryAgain()
{
	int r;

	if(d->server) {
		if(!d->tried) {
			r = d->c->nextStep(d->stepData);
			d->tried = true;
		}
		else {
			r = d->c->tryAgain();
		}

		if(r == TQCA_SASLContext::Error) {
			error(ErrAuth);
			return;
		}
		else if(r == TQCA_SASLContext::Continue) {
			d->tried = false;
			nextStep(d->c->result());
			return;
		}
		else if(r == TQCA_SASLContext::AuthCheck) {
			authCheck(d->c->username(), d->c->authzid());
			return;
		}
	}
	else {
		if(d->first) {
			if(!d->tried) {
				r = d->c->clientFirstStep(d->allowCSF);
				d->tried = true;
			}
			else
				r = d->c->tryAgain();

			if(r == TQCA_SASLContext::Error) {
				error(ErrAuth);
				return;
			}
			else if(r == TQCA_SASLContext::NeedParams) {
				//d->tried = false;
				TQCA_SASLNeedParams np = d->c->clientParamsNeeded();
				needParams(np.user, np.authzid, np.pass, np.realm);
				return;
			}

			TQString mech = d->c->mech();
			const TQByteArray *clientInit = d->c->clientInit();

			d->first = false;
			d->tried = false;
			clientFirstStep(mech, clientInit);
		}
		else {
			if(!d->tried) {
				r = d->c->nextStep(d->stepData);
				d->tried = true;
			}
			else
				r = d->c->tryAgain();

			if(r == TQCA_SASLContext::Error) {
				error(ErrAuth);
				return;
			}
			else if(r == TQCA_SASLContext::NeedParams) {
				//d->tried = false;
				TQCA_SASLNeedParams np = d->c->clientParamsNeeded();
				needParams(np.user, np.authzid, np.pass, np.realm);
				return;
			}
			d->tried = false;
			//else if(r == TQCA_SASLContext::Continue) {
				nextStep(d->c->result());
			//	return;
			//}
		}
	}

	if(r == TQCA_SASLContext::Success)
		authenticated();
	else if(r == TQCA_SASLContext::Error)
		error(ErrAuth);
}

int SASL::ssf() const
{
	return d->c->security();
}

void SASL::write(const TQByteArray &a)
{
	TQByteArray b;
	if(!d->c->encode(a, &b)) {
		error(ErrCrypt);
		return;
	}
	int oldsize = d->outbuf.size();
	d->outbuf.resize(oldsize + b.size());
	memcpy(d->outbuf.data() + oldsize, b.data(), b.size());
	readyReadOutgoing(a.size());
}

TQByteArray SASL::read()
{
	TQByteArray a = d->inbuf.copy();
	d->inbuf.resize(0);
	return a;
}

void SASL::writeIncoming(const TQByteArray &a)
{
	TQByteArray b;
	if(!d->c->decode(a, &b)) {
		error(ErrCrypt);
		return;
	}
	int oldsize = d->inbuf.size();
	d->inbuf.resize(oldsize + b.size());
	memcpy(d->inbuf.data() + oldsize, b.data(), b.size());
	readyRead();
}

TQByteArray SASL::readOutgoing()
{
	TQByteArray a = d->outbuf.copy();
	d->outbuf.resize(0);
	return a;
}

#include "tqca.moc"
