/*
 * accountdlg.cpp - dialogs for manipulating PsiAccounts
 * Copyright (C) 2001, 2002  Justin Karneges
 *
 * 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include"accountdlg.h"

#include<qpushbutton.h>
#include<qlistview.h>
#include<qlineedit.h>
#include<qcheckbox.h>
#include<qlabel.h>
#include<qlayout.h>
#include<qgroupbox.h>
#include<qcombobox.h>
#include<qradiobutton.h>
#include<qbuttongroup.h>
#include<qtabwidget.h>
#include<qwhatsthis.h>
#include<qurl.h>
#include<qca.h>
#include"psicon.h"
#include"psiaccount.h"
#include"common.h"
#include"im.h"
#include"xmpp_tasks.h"
#include"proxy.h"
#include"sslcertdlg.h"
#include"busywidget.h"
#include"iconwidget.h"
#include"fancylabel.h"
#include"base64.h"

using namespace XMPP;

//----------------------------------------------------------------------------
// MiniClient
//----------------------------------------------------------------------------
static QCA::Cert readCertXml(const QDomElement &e)
{
	QCA::Cert cert;
	// there should be one child data tag
	QDomElement data = e.elementsByTagName("data").item(0).toElement();
	if(!data.isNull())
		cert.fromDER(Base64::stringToArray(data.text()));
	return cert;
}

static QPtrList<QCA::Cert> getRootCerts(const QStringList &stores)
{
	QPtrList<QCA::Cert> list;

	for(QStringList::ConstIterator dit = stores.begin(); dit != stores.end(); ++dit) {
		QDir dir(*dit);
		dir.setNameFilter("*.xml");
		QStringList entries = dir.entryList();
		for(QStringList::ConstIterator it = entries.begin(); it != entries.end(); ++it) {
			QFile f(dir.filePath(*it));
			if(!f.open(IO_ReadOnly))
				continue;
			QDomDocument doc;
			bool ok = doc.setContent(&f);
			f.close();
			if(!ok)
				continue;

			QDomElement base = doc.documentElement();
			if(base.tagName() != "store")
				continue;
			QDomNodeList cl = base.elementsByTagName("certificate");

			int num = 0;
			for(int n = 0; n < (int)cl.count(); ++n) {
				QCA::Cert *cert = new QCA::Cert(readCertXml(cl.item(n).toElement()));
				if(cert->isNull()) {
					delete cert;
					continue;
				}

				++num;
				list.append(cert);
			}
		}
	}

	return list;
}

MiniClient::MiniClient(QObject *parent)
:QObject(parent)
{
	_client = new Client;
	conn = 0;
	tls = 0;
	tlsHandler = 0;
	stream = 0;
	certList.setAutoDelete(true);
}

MiniClient::~MiniClient()
{
	delete _client;
	reset();
}

void MiniClient::reset()
{
	delete stream;
	stream = 0;

	delete tls;
	tls = 0;
	tlsHandler = 0;

	delete conn;
	conn = 0;

	certList.clear();
}

void MiniClient::connectToServer(const Jid &jid, bool ssl, const QString &_host, int _port, ProxyManager *pm, int proxy, QString *_pass)
{
	j = jid;

	QString host;
	int port;
	if(!_host.isEmpty()) {
		host = _host;
		port = _port;
	}
	else {
		host = jid.host();
		if(ssl)
			port = 5223;
		else
			port = 5222;
	}

	AdvancedConnector::Proxy p;
	if(proxy > 0) {
		const ProxyItem &pi = pm->getItem(proxy-1);
		if(pi.type == "http") // HTTP Connect
			p.setHttpConnect(pi.settings.host, pi.settings.port);
		else if(pi.type == "socks") // SOCKS
			p.setSocks(pi.settings.host, pi.settings.port);
		else if(pi.type == "poll") { // HTTP Poll
			QUrl u = pi.settings.url;
			if(u.query().isEmpty()) {
				QString v = host + ':' + QString::number(port);
				QUrl::encode(v);
				u.setQuery(QString("server=") + v);
			}
			p.setHttpPoll(pi.settings.host, pi.settings.port, u.toString());
			p.setPollInterval(2);
		}

		if(pi.settings.useAuth)
			p.setUserPass(pi.settings.user, pi.settings.pass);
	}

	conn = new AdvancedConnector;
	if(ssl) {
		QStringList certDirs;
		certDirs += g.pathBase + "/certs";
		certList = getRootCerts(certDirs);

		tls = new QCA::TLS;
		tls->setCertificateStore(certList);
		tlsHandler = new QCATLSHandler(tls);
		connect(tlsHandler, SIGNAL(tlsHandshaken()), SLOT(tls_handshaken()));
	}
	conn->setProxy(p);
	conn->setOptHostPort(host, port);
	conn->setOptSSL(ssl);

	stream = new ClientStream(conn, tlsHandler);
	stream->setOldOnly(true);
	connect(stream, SIGNAL(connected()), SLOT(cs_connected()));
	connect(stream, SIGNAL(securityLayerActivated(int)), SLOT(cs_securityLayerActivated(int)));
	connect(stream, SIGNAL(needAuthParams(bool, bool, bool)), SLOT(cs_needAuthParams(bool, bool, bool)));
	connect(stream, SIGNAL(authenticated()), SLOT(cs_authenticated()));
	connect(stream, SIGNAL(connectionClosed()), SLOT(cs_connectionClosed()));
	connect(stream, SIGNAL(delayedCloseFinished()), SLOT(cs_delayedCloseFinished()));
	connect(stream, SIGNAL(warning(int)), SLOT(cs_warning(int)));
	connect(stream, SIGNAL(error(int)), SLOT(cs_error(int)));

	if(_pass) {
		pass = *_pass;
		_client->connectToServer(stream, j);
	}
	else
		_client->connectToServer(stream, j, false);
}

void MiniClient::close()
{
	_client->close();
	reset();
}

Client *MiniClient::client()
{
	return _client;
}

void MiniClient::tls_handshaken()
{
	QCA::Cert cert = tls->peerCertificate();
	int r = tls->certificateValidityResult();
	if(r != QCA::TLS::Valid) {
		QString str = PsiAccount::resultToString(r);
		while(1) {
			int n = QMessageBox::warning(0,
				tr("Server Authentication"),
				tr("The %1 certificate failed the authenticity test.").arg(j.host()) + '\n' + tr("Reason: %1").arg(str),
				tr("&Details..."),
				tr("Co&ntinue"),
				tr("&Cancel"), 0, 2);
			if(n == 0) {
				SSLCertDlg::showCert(cert, r);
			}
			else if(n == 1) {
				tlsHandler->continueAfterHandshake();
				break;
			}
			else if(n == 2) {
				close();
				error();
				break;
			}
		}
	}
	else
		tlsHandler->continueAfterHandshake();
}

void MiniClient::cs_connected()
{
}

void MiniClient::cs_securityLayerActivated(int)
{
}

void MiniClient::cs_needAuthParams(bool, bool, bool)
{
	stream->setPassword(pass);
	stream->continueAfterParams();
}

void MiniClient::cs_authenticated()
{
	_client->start(j.host(), j.user(), "", "");
	handshaken();
}

void MiniClient::cs_connectionClosed()
{
	cs_error(-1);
}

void MiniClient::cs_delayedCloseFinished()
{
}

void MiniClient::cs_warning(int)
{
	stream->continueAfterWarning();
}

void MiniClient::cs_error(int err)
{
	QString str;
	bool reconn;

	PsiAccount::getErrorInfo(err, conn, stream, tlsHandler, &str, &reconn);
	close();

	QMessageBox::critical(0, tr("Server Error"), tr("There was an error communicating with the Jabber server.\nDetails: %1").arg(str));
	error();
}

//----------------------------------------------------------------------------
// AccountManageDlg
//----------------------------------------------------------------------------
class AccountManageItem : public QCheckListItem
{
public:
	AccountManageItem(QListView *par, PsiAccount *_pa)
	:QCheckListItem(par,"",CheckBox)
	{
		pa = _pa;
		updateInfo();
	}

	void updateInfo()
	{
		const UserAccount &acc = pa->userAccount();
		setText(0, pa->name());
		//setPixmap(0, IconsetFactory::icon("psi/account"));
		Jid j = acc.jid;
		setText(1, acc.opt_host ? acc.host : j.host());
		setText(2, pa->isActive() ? AccountManageDlg::tr("Active") : AccountManageDlg::tr("Not active"));
		setOn(pa->enabled());
	}

	void stateChange( bool s)
	{
		if (pa->enabled()!=s)
			pa->setEnabled(s);
		updateInfo();
	}

	int rtti() const
	{
		return 8109;
	}

	PsiAccount *pa;
};

AccountManageDlg::AccountManageDlg(PsiCon *_psi, const char *name)
:AccountManageUI(0, name, false, WDestructiveClose)
{
	psi = _psi;
	psi->dialogRegister(this);

	setCaption(CAP(caption()));

	// setup signals
	connect(pb_add, SIGNAL(clicked()), SLOT(add()));
	connect(pb_modify, SIGNAL(clicked()), SLOT(modify()));
	connect(pb_remove, SIGNAL(clicked()), SLOT(remove()));
	connect(pb_close, SIGNAL(clicked()), SLOT(close()));

	connect(lv_accs, SIGNAL(doubleClicked(QListViewItem *)), SLOT(modify(QListViewItem *)));
	connect(lv_accs, SIGNAL(selectionChanged(QListViewItem *)), SLOT(qlv_selectionChanged(QListViewItem *)));
	connect(psi, SIGNAL(accountAdded(PsiAccount *)), SLOT(accountAdded(PsiAccount *)));
	connect(psi, SIGNAL(accountUpdated(PsiAccount *)), SLOT(accountUpdated(PsiAccount *)));
	connect(psi, SIGNAL(accountRemoved(PsiAccount *)), SLOT(accountRemoved(PsiAccount *)));

	lv_accs->setAllColumnsShowFocus(true);
	lv_accs->setResizeMode(QListView::LastColumn);

	PsiAccountListIt it(psi->accountList());
	for(PsiAccount *pa; (pa = it.current()); ++it) {
		new AccountManageItem(lv_accs, pa);
	}

	if(lv_accs->childCount() > 0)
		lv_accs->setSelected(lv_accs->firstChild(), true);
	else
		qlv_selectionChanged(0);
}

AccountManageDlg::~AccountManageDlg()
{
	psi->dialogUnregister(this);
}

void AccountManageDlg::qlv_selectionChanged(QListViewItem *lvi)
{
	AccountManageItem *i = (AccountManageItem *)lvi;
	bool ok = i ? true: false;

	pb_modify->setEnabled(ok);
	pb_remove->setEnabled(ok);
}

void AccountManageDlg::add()
{
	AccountAddDlg *w = new AccountAddDlg(psi, 0);
	w->show();
}

void AccountManageDlg::modify()
{
	modify(lv_accs->currentItem());
}

void AccountManageDlg::modify(QListViewItem *lvi)
{
	AccountManageItem *i = (AccountManageItem *)lvi;
	if(!i)
		return;

	psi->modifyAccount(i->pa);
}

void AccountManageDlg::remove()
{
	AccountManageItem *i = (AccountManageItem *)lv_accs->currentItem();
	if(!i)
		return;

	if(i->pa->isActive()) {
		QMessageBox::information(this, tr("Error"), tr("Unable to remove the account, as it is currently active."));
		return;
	}

	AccountRemoveDlg *w = new AccountRemoveDlg(psi->proxy(), i->pa->userAccount());
	int n = w->exec();
	if(n != QDialog::Accepted) {
		delete w;
		return;
	}
	delete w;
	psi->removeAccount(i->pa);
}

void AccountManageDlg::accountAdded(PsiAccount *pa)
{
	new AccountManageItem(lv_accs, pa);
}

void AccountManageDlg::accountUpdated(PsiAccount *pa)
{
	AccountManageItem *i;
	QListViewItemIterator it(lv_accs);
	for(; (i = (AccountManageItem *)it.current()) ; ++it) {
		if(i->pa == pa)
			break;
	}
	if(!i)
		return;

	i->updateInfo();
}

void AccountManageDlg::accountRemoved(PsiAccount *pa)
{
	AccountManageItem *i;
	QListViewItemIterator it(lv_accs);
	for(; (i = (AccountManageItem *)it.current()) ; ++it) {
		if(i->pa == pa)
			break;
	}
	if(!i)
		return;

	delete i;

	qlv_selectionChanged(lv_accs->currentItem());
}


//----------------------------------------------------------------------------
// AccountAddDlg
//----------------------------------------------------------------------------
AccountAddDlg::AccountAddDlg(PsiCon *_psi, QWidget *parent, const char *name)
:AccountAddUI(parent, name, false, WDestructiveClose)
{
	psi = _psi;
	psi->dialogRegister(this);

	setCaption(CAP(caption()));

	connect(pb_close, SIGNAL(clicked()), SLOT(close()));
	connect(pb_add, SIGNAL(clicked()), SLOT(add()));
	connect(le_name, SIGNAL(textChanged(const QString &)), SLOT(setAddButton(const QString &)));

	QWhatsThis::add(ck_reg,
		tr("Check this option if you don't yet have a Jabber account "
		"and you want to register one.  Note that this will only work "
		"on servers that allow anonymous registration."));

	QString def = tr("Default");
	QString aname = def;
	int n = 0;
	while(1) {
		bool taken = false;
		PsiAccountListIt it(psi->accountList());
		for(PsiAccount *pa; (pa = it.current()); ++it) {
			if(aname == pa->name()) {
				taken = true;
				break;
			}
		}

		if(!taken)
			break;
		aname = def + '_' + QString::number(++n);
	}

	le_name->setText(aname);
	le_name->setFocus();
}

AccountAddDlg::~AccountAddDlg()
{
	psi->dialogUnregister(this);
}

void AccountAddDlg::add()
{
	QString def = le_name->text();
	QString aname = def;
	int n = 0;
	while(1) {
		bool taken = false;
		PsiAccountListIt it(psi->accountList());
		for(PsiAccount *pa; (pa = it.current()); ++it) {
			if(aname == pa->name()) {
				taken = true;
				break;
			}
		}

		if(!taken)
			break;
		aname = def + '_' + QString::number(++n);
	}
	le_name->setText( aname );

	if(ck_reg->isChecked()) {
		AccountRegDlg *w = new AccountRegDlg(psi->proxy(), this);
		int n = w->exec();
		if(n != QDialog::Accepted) {
			delete w;
			return;
		}

		Jid jid = w->jid;
		QString pass = w->pass;
		bool opt_host = w->opt_host;
		QString host = w->sp_host;
		int port = w->sp_port;
		bool ssl = w->ssl;
		int proxy = w->proxy;

		delete w;

		psi->createAccount(le_name->text(), jid, pass, opt_host, host, port, ssl, proxy);
	}
	else {
		psi->createAccount(le_name->text());
	}

	close();
}

void AccountAddDlg::setAddButton(const QString &s)
{
	pb_add->setEnabled(!s.isEmpty());
}


//----------------------------------------------------------------------------
// AccountModifyDlg
//----------------------------------------------------------------------------
AccountModifyDlg::AccountModifyDlg(PsiAccount *_pa, QWidget *parent, const char *name)
:AccountModifyUI(parent, name, false, WDestructiveClose)
{
	pa = _pa;
	connect(pa->psi(), SIGNAL(pgpToggled(bool)), SLOT(pgpToggled(bool)));
	pa->dialogRegister(this);

	setCaption(CAP(caption()));
	setIcon(IconsetFactory::icon("psi/account"));

	const UserAccount &acc = pa->userAccount();

	connect(pb_close, SIGNAL(clicked()), SLOT(reject()));
	connect(ck_host, SIGNAL(toggled(bool)), SLOT(hostToggled(bool)));
	connect(pb_key, SIGNAL(clicked()), SLOT(chooseKey()));
	connect(pb_keyclear, SIGNAL(clicked()), SLOT(clearKey()));
	connect(pb_save, SIGNAL(clicked()), SLOT(save()));

	le_pass->setEnabled(true);
	le_host->setEnabled(false);
	le_port->setEnabled(false);

	gb_pgp->setEnabled(false);

	connect(pb_vcard, SIGNAL(clicked()), SLOT(detailsVCard()));
	connect(pb_changepw, SIGNAL(clicked()), SLOT(detailsChangePW()));

	le_name->setText(acc.name);
	le_jid->setText(acc.jid);

	ck_ssl->setChecked(acc.opt_ssl);
	connect(ck_ssl, SIGNAL(toggled(bool)), SLOT(sslToggled(bool)));

	if (acc.opt_pass)
		le_pass->setText(acc.pass);

	ck_host->setChecked(acc.opt_host);
	le_host->setText(acc.host);
	le_port->setText(QString::number(acc.port));

	le_resource->setText(acc.resource);
	le_priority->setText(QString::number(acc.priority));

	ck_plain->setChecked(acc.opt_plain);
	ck_auto->setChecked(acc.opt_auto);
	ck_reconn->setChecked(acc.opt_reconn);
	ck_log->setChecked(acc.opt_log);
	ck_keepAlive->setChecked(acc.opt_keepAlive);
	ck_ignoreSSLWarnings->setChecked(acc.opt_ignoreSSLWarnings);
	le_dtProxy->setText(acc.dtProxy.full());

	keyID = acc.pgpSecretKeyID;
	updateUserID();
	if(pa->psi()->pgp()) {
		gb_pgp->setEnabled(true);
	}

	pc = pa->psi()->proxy()->createProxyChooser(gb_proxy);
	replaceWidget(lb_proxychooser, pc);
	pc->setCurrentItem(acc.proxy_index);

	if(le_name->text().isEmpty())
		le_name->setFocus();
	else if(le_jid->text().isEmpty())
		le_jid->setFocus();
	else
		pb_save->setFocus();

	// QWhatsThis helpers
	QWhatsThis::add(ck_plain,
		tr("Normally, Psi logs in using the <i>digest</i> authentication "
		"method.  Check this box to force a plain text login "
		"to the Jabber server. Use this option only if you have "
		"problems connecting with the normal login procedure, as it "
		"makes your connection potentially vulnerable to "
		"attacks."));
	QWhatsThis::add(ck_auto,
		tr("Automatically login to this account on Psi startup.  Useful if "
		"you have Psi automatically launched when an Internet "
		"connection is detected."));
	QWhatsThis::add(ck_reconn,
		tr("Makes Psi try to reconnect if the connection was broken.  "
		"Useful, if you have an unstable connection and have to "
		"reconnect often."));
	QWhatsThis::add(ck_log,
		tr("Keep a log of message history.  Disable this "
		"option if you want to conserve disk space or if you need "
		"maximum security."));
	QWhatsThis::add(ck_keepAlive,
		tr("Sends so called \"Keep-alive\" packets periodically.  "
		"It is useful if your connection is set to be "
		"automatically disconnected after a certain period of "
		"inactivity (for example, by your ISP) and you want to keep it "
		"up all the time."));
	QWhatsThis::add(ck_ignoreSSLWarnings,
		tr("Ignores all the SSL warnings, for example, about "
		"incorrect certificates.  Useful if your server doesn't "
		"use a validated SSL certificate and you are "
		"annoyed with warnings."));
	QWhatsThis::add(ck_ssl,
		tr("Check this option to use an encrypted SSL connection to "
		"the Jabber server.  You may use this option if your "
		"server supports it and if you have the necessary QSSL "
		"plugin installed.  For more information, check the "
		"Psi homepage."));
	QWhatsThis::add(ck_host,
		tr("Use this option for manual configuration of your Jabber host "
		"if it is not the same as the host you are connecting to.  This option is mostly useful "
		"if you have some sort of proxy route on your "
		"local machine (i.e. you connect to localhost), but your "
		"account is registered on an external server."));
	QWhatsThis::add(le_resource,
		tr("You can have multiple clients connected to the Jabber server "
		"with your single account.  Each login is distinguished by a \"resource\" "
		"name, which you can specify in this field."));
	QWhatsThis::add(le_priority,
		tr("<p>You can have multiple clients connected to the Jabber "
		"server with your single account.  In such a situation, "
		"the client with the highest priority (that is specified in "
		"this field) will be the one that will receive "
		"all incoming events.</p>"
		"<p>For example, if you have a permanent connection "
		"to the Internet at your work location, and have a dial-up at home, "
		"you can have your Jabber client permanently running at work "
		"with a low priority, and you can still use the same account "
		"from home, using a client with higher priority to "
		"temporary \"disable\" the lower priority client at work.</p>"));

	resize(minimumSize());
}

AccountModifyDlg::~AccountModifyDlg()
{
	pa->dialogUnregister(this);
}

void AccountModifyDlg::updateUserID()
{
	if(keyID.isEmpty()) {
		setKeyID(false);
	}
	else {
		QString userID = QString::null;
		if(pa->psi()->pgp()) {
			OpenPGP::KeyList list = pa->psi()->pgp()->secretKeys();
			for(OpenPGP::KeyList::ConstIterator it = list.begin(); it != list.end(); ++it) {
				const OpenPGP::Key &k = *it;
				if(k.keyID() == keyID) {
					userID = k.userID();
					break;
				}
			}
		}
		if(userID.isNull())
			setKeyID(true, tr("Unknown Key: %1").arg(keyID.left(8)));
		else
			setKeyID(true, userID);
	}
}

void AccountModifyDlg::setKeyID(bool b, const QString &s)
{
	if(b) {
		lb_keyname->setText(s);
		lb_keyname->setMinimumWidth(100);
		lb_keyicon->setEnabled(true);
		lb_keyname->setEnabled(true);
		pb_keyclear->setEnabled(true);
	}
	else {
		lb_keyname->setText(tr("No Key Selected"));
		lb_keyicon->setEnabled(false);
		lb_keyname->setEnabled(false);
		pb_keyclear->setEnabled(false);
	}
}

void AccountModifyDlg::pgpToggled(bool b)
{
	if(b) {
		gb_pgp->setEnabled(true);
	}
	else {
		gb_pgp->setEnabled(false);
	}
	updateUserID();
}

void AccountModifyDlg::setPassword(const QString &pw)
{
	if (!le_pass->text().isEmpty())
		le_pass->setText(pw);
}

void AccountModifyDlg::sslToggled(bool on)
{
	if(on && !QCA::isSupported(QCA::CAP_TLS)) {
		QMessageBox::information(this, tr("SSL error"), tr("Cannot enable SSL/TLS.  Plugin not found."));
		ck_ssl->setChecked(false);
		return;
	}

	le_port->setText(on ? "5223": "5222");
}

void AccountModifyDlg::hostToggled(bool on)
{
	le_host->setEnabled(on);
	le_port->setEnabled(on);
}

void AccountModifyDlg::chooseKey()
{
	OpenPGP::KeyList list = pa->psi()->pgp()->secretKeys();
	PGPKeyDlg *w = new PGPKeyDlg(list, keyID, this);
	w->setCaption(tr("Secret Key"));
	int r = w->exec();
	QString key;
	if(r == QDialog::Accepted)
		key = w->keyID();
	delete w;

	if(!key.isEmpty()) {
		keyID = key;
		updateUserID();
	}
}

void AccountModifyDlg::clearKey()
{
	setKeyID(false);
	keyID = "";
}

void AccountModifyDlg::detailsVCard()
{
	pa->changeVCard();
}

void AccountModifyDlg::detailsChangePW()
{
	pa->changePW();
}

void AccountModifyDlg::save()
{
	UserAccount acc = pa->userAccount();

	if(le_name->text().isEmpty()) {
		QMessageBox::information(this, tr("Error"), tr("You must specify a name for the account before you may save it."));
		return;
	}

	Jid newJid( le_jid->text() );
	if ( newJid.user().isEmpty() || newJid.host().isEmpty() ) {
		QMessageBox::information(this, tr("Error"), tr("<i>Jabber ID</i> must be specified in the format <i>user@host</i>."));
		return;
	}

	// do not allow duplicate account names
	QString def = le_name->text();
	QString aname = def;
	int n = 0;
	{
		PsiAccountListIt it(pa->psi()->accountList());
		for(PsiAccount *pa; (pa = it.current()); ++it)
			if(aname == pa->name())
				n++;
	}

	if ( aname == acc.name )
		n--;

	if ( n )
		aname = def + '_' + QString::number(++n);
	le_name->setText( aname );

	acc.name = le_name->text();
	acc.jid = le_jid->text();
	acc.pass = le_pass->text();
	acc.opt_pass = !acc.pass.isEmpty();

	acc.opt_host = ck_host->isChecked();
	acc.host = le_host->text();
	acc.port = le_port->text().toInt();

	acc.resource = le_resource->text();
	acc.priority = le_priority->text().toInt();

	acc.opt_ssl = ck_ssl->isChecked();
	acc.opt_plain = ck_plain->isChecked();
	acc.opt_auto = ck_auto->isChecked();
	acc.opt_reconn = ck_reconn->isChecked();
	acc.opt_log = ck_log->isChecked();
	acc.opt_keepAlive = ck_keepAlive->isChecked();
	acc.opt_ignoreSSLWarnings = ck_ignoreSSLWarnings->isChecked();
	acc.dtProxy = le_dtProxy->text();

	acc.pgpSecretKeyID = keyID;

	acc.proxy_index = pc->currentItem();

	if(pa->isActive()) {
		QMessageBox::information(this, tr("Warning"), tr("This account is currently active, so certain changes may not take effect until the next login."));
	}

	pa->setUserAccount(acc);

	accept();
}


//----------------------------------------------------------------------------
// AccountRegDlg
//----------------------------------------------------------------------------
AccountRegDlg::AccountRegDlg(ProxyManager *_proxyman, QWidget *parent, const char *name)
:AccountRegUI(parent, name, false)
{
	setCaption(CAP(caption()));

	le_host->setEnabled(false);
	le_port->setEnabled(false);

	connect(pb_reg, SIGNAL(clicked()), SLOT(reg()));
	connect(ck_ssl, SIGNAL(toggled(bool)), SLOT(sslToggled(bool)));
	connect(ck_host, SIGNAL(toggled(bool)), SLOT(hostToggled(bool)));

	proxyman = _proxyman;
	pc = proxyman->createProxyChooser(gb_proxy);
	replaceWidget(lb_proxychooser, pc);
	pc->fixTabbing(le_confirm, ck_ssl);
	pc->setCurrentItem(0);

	le_port->setText("5222");
	le_host->setFocus();

	client = new MiniClient;
	connect(client, SIGNAL(handshaken()), SLOT(client_handshaken()));
	connect(client, SIGNAL(error()), SLOT(client_error()));
}

AccountRegDlg::~AccountRegDlg()
{
	delete client;
}

/*void AccountRegDlg::closeEvent(QCloseEvent *e)
{
	e->ignore();
	reject();
}*/

void AccountRegDlg::done(int r)
{
	if(busy->isActive()) {
		int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to cancel the registration?"), tr("&Yes"), tr("&No"));
		if(n != 0)
			return;
	}
	QDialog::done(r);
}

void AccountRegDlg::sslToggled(bool on)
{
	if(on && !QCA::isSupported(QCA::CAP_TLS)) {
		QMessageBox::information(this, tr("SSL error"), tr("Cannot enable SSL/TLS.  Plugin not found."));
		ck_ssl->setChecked(false);
		return;
	}

	le_port->setText(on ? "5223": "5222");
}

void AccountRegDlg::hostToggled(bool on)
{
	le_host->setEnabled(on);
	le_port->setEnabled(on);
}

void AccountRegDlg::reg()
{
	// sanity check
	Jid j(le_jid->text());
	if ( j.user().isEmpty() || j.host().isEmpty() ) {
		QMessageBox::information(this, tr("Error"), tr("<i>Jabber ID</i> must be specified in the format <i>user@host</i>."));
		return;
	}

	if(le_pass->text().isEmpty()) {
		QMessageBox::information(this, tr("Error"), tr("You must fill out the fields properly before you can register."));
		return;
	}

	if(le_pass->text() != le_confirm->text()) {
		QMessageBox::information(this, tr("Error"), tr("Password and confirmation do not match.  Please enter them again."));
		le_pass->setText("");
		le_confirm->setText("");
		le_pass->setFocus();
		return;
	}

	busy->start();
	block();

	jid = j;
	ssl = ck_ssl->isChecked();
	pass = le_pass->text();
	opt_host = ck_host->isChecked();
	sp_host = le_host->text();
	sp_port = le_port->text().toInt();

	client->connectToServer(jid, ssl, opt_host ? sp_host : QString(), sp_port, proxyman, pc->currentItem(), 0);
}

void AccountRegDlg::client_handshaken()
{
	// try to register an account
	JT_Register *reg = new JT_Register(client->client()->rootTask());
	connect(reg, SIGNAL(finished()), SLOT(reg_finished()));
	reg->reg(jid.user(), pass);
	reg->go(true);
}

void AccountRegDlg::client_error()
{
	busy->stop();
	unblock();
}

void AccountRegDlg::reg_finished()
{
	JT_Register *reg = (JT_Register *)sender();

	client->close();
	busy->stop();

	if(reg->success()) {
		QMessageBox::information(this, tr("Success"), tr("The account was registered successfully."));
		proxy = pc->currentItem();
		accept();
		return;
	}
	else if(reg->statusCode() != Task::ErrDisc) {
		unblock();
		QMessageBox::critical(this, tr("Error"), QString(tr("There was an error registering the account.\nReason: %1")).arg(reg->statusString()));
	}
}

void AccountRegDlg::block()
{
	gb_account->setEnabled(false);
	gb_proxy->setEnabled(false);
	gb_advanced->setEnabled(false);
	pb_reg->setEnabled(false);
}

void AccountRegDlg::unblock()
{
	gb_account->setEnabled(true);
	gb_proxy->setEnabled(true);
	gb_advanced->setEnabled(true);
	pb_reg->setEnabled(true);
	le_jid->setFocus();
}


//----------------------------------------------------------------------------
// AccountRemoveDlg
//----------------------------------------------------------------------------
class AccountRemoveDlg::Private
{
public:
	Private() {}

	UserAccount acc;
	QButtonGroup *bg;
	ProxyManager *proxyman;
};

AccountRemoveDlg::AccountRemoveDlg(ProxyManager *proxyman, const UserAccount &acc, QWidget *parent, const char *name)
:AccountRemoveUI(parent, name, false)
{
	d = new Private;
	d->acc = acc;
	d->proxyman = proxyman;

	setCaption(CAP(caption()));

	connect(pb_close, SIGNAL(clicked()), SLOT(close()));
	connect(pb_remove, SIGNAL(clicked()), SLOT(remove()));

	d->bg = new QButtonGroup(0);
	d->bg->insert(rb_remove, 0);
	d->bg->insert(rb_removeAndUnreg, 1);
	connect(d->bg, SIGNAL(clicked(int)), SLOT(bg_clicked(int)));
	d->bg->setButton(0);
	bg_clicked(0);

	pb_close->setFocus();

	client = new MiniClient;
	connect(client, SIGNAL(handshaken()), SLOT(client_handshaken()));
	connect(client, SIGNAL(error()), SLOT(client_error()));
}

AccountRemoveDlg::~AccountRemoveDlg()
{
	delete client;

	delete d->bg;
	delete d;
}

/*void AccountRemoveDlg::closeEvent(QCloseEvent *e)
{
	e->ignore();
	reject();
}*/

void AccountRemoveDlg::done(int r)
{
	if(busy->isActive()) {
		int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to cancel the unregistration?"), tr("&Yes"), tr("&No"));
		if(n != 0)
			return;
	}
	QDialog::done(r);
}

void AccountRemoveDlg::bg_clicked(int x)
{
	if(x == 0) {
		lb_pass->setEnabled(false);
		le_pass->setEnabled(false);
	}
	else if(x == 1) {
		lb_pass->setEnabled(true);
		le_pass->setEnabled(true);
		le_pass->setFocus();
	}
}

void AccountRemoveDlg::remove()
{
	bool unreg = rb_removeAndUnreg->isChecked();

	if(unreg) {
		if(le_pass->text() != d->acc.pass) {
			QMessageBox::information(this, tr("Error"), tr("Password does not match account.  Please try again."));
			le_pass->setFocus();
			return;
		}
	}

	int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to remove <b>%1</b> ?").arg(d->acc.name), tr("&Yes"), tr("&No"));
	if(n != 0)
		return;

	if(!unreg) {
		accept();
		return;
	}

	busy->start();
	gb_account->setEnabled(false);
	pb_remove->setEnabled(false);

	Jid j = d->acc.jid;
	j.setResource(d->acc.resource);
	client->connectToServer(j, d->acc.opt_ssl, d->acc.opt_host ? d->acc.host : QString(), d->acc.port, d->proxyman, d->acc.proxy_index, &d->acc.pass);
}

void AccountRemoveDlg::client_handshaken()
{
	// try to unregister an account
	JT_Register *reg = new JT_Register(client->client()->rootTask());
	connect(reg, SIGNAL(finished()), SLOT(unreg_finished()));
	reg->unreg();
	reg->go(true);
}

void AccountRemoveDlg::client_error()
{
	busy->stop();
	gb_account->setEnabled(true);
	pb_remove->setEnabled(true);
}

void AccountRemoveDlg::unreg_finished()
{
	JT_Register *reg = (JT_Register *)sender();

	client->close();
	busy->stop();

	if(reg->success()) {
		QMessageBox::information(this, tr("Success"), tr("The account was unregistered successfully."));
		accept();
		return;
	}
	else if(reg->statusCode() != Task::ErrDisc) {
		gb_account->setEnabled(true);
		pb_remove->setEnabled(true);
		QMessageBox::critical(this, tr("Error"), QString(tr("There was an error unregistering the account.\nReason: %1")).arg(reg->statusString()));
	}
}
