/***************************************************************************
 *
 * knetworkmanager-connection_setting_8021x.cpp - A NetworkManager frontend for KDE
 *
 * Copyright (C) 2005, 2006 Novell, Inc.
 *
 * Author: Helmut Schaa <hschaa@suse.de>, <helmut.schaa@gmx.de>
 *
 * 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
 *
 **************************************************************************/

/* qt headers */
#include <qhostaddress.h>
#include <qvariant.h>

/* kde headers */
#include <kdebug.h>
#include <klocale.h>

/* QDBus headers*/
#include <dbus/qdbusdata.h>
#include <dbus/qdbusdatamap.h>

/* knetworkmanager headers */
#include "knetworkmanager.h"
#include "knetworkmanager-connection_setting_8021x.h"
#include "knetworkmanager-accesspoint.h"
#include "knetworkmanager-connection_setting_wireless.h"
#include "knetworkmanager-connection.h"
#include "sha1.h"
#include "md5.h"

#define WPA_PMK_LEN 32

using namespace ConnectionSettings;

/*
	class IEEE8021x
*/
IEEE8021x::IEEE8021x(Connection* conn)
	: ConnectionSetting(conn, NM_SETTING_802_1X_SETTING_NAME)
	, _eap(EAP_PHASE1_NONE)
	, _identity(QString::null)
	, _anonIdentity(QString::null)
	, _caPath(QString::null)
	, _phase1PeapVer(QString::null)
	, _phase1PeapLabel(QString::null)
	, _phase1FastProvisioning(QString::null)
	, _eapPhase2(EAP_PHASE2_AUTH_NONE)
	, _phase2AuthEAP(QString::null)
	, _phase2CaPath(QString::null)
	, _useSystemCaCert(false)
{
	// init eap map
	_eapMap[EAP_NONE] = QString::null;
	_eapMap[EAP_LEAP] = "leap";
	_eapMap[EAP_MD5] = "md5";
	_eapMap[EAP_PAP] = "pap";
	_eapMap[EAP_CHAP] = "chap";
	_eapMap[EAP_MSCHAP] = "mschap";
	_eapMap[EAP_MSCHAPV2] = "mschapv2";
	_eapMap[EAP_FAST] = "fast";
	_eapMap[EAP_PSK] = "psk";
	_eapMap[EAP_PAX] = "pax";
	_eapMap[EAP_SAKE] = "sake";
	_eapMap[EAP_GPSK] = "gpsk";
	_eapMap[EAP_TLS] = "tls";
	_eapMap[EAP_PEAP] = "peap";
	_eapMap[EAP_TTLS] = "ttls";
	_eapMap[EAP_SIM] = "sim";
	_eapMap[EAP_GTC] = "gtc";
	_eapMap[EAP_OTP] = "otp";
}

QString
IEEE8021x::getIdentity(void) const
{
	return _identity;
}

void
IEEE8021x::setIdentity(const QString & identity)
{
	_identity = identity;
}

QString
IEEE8021x::getAnonIdentity(void) const
{
	return _anonIdentity;
}

void
IEEE8021x::setAnonIdentity(const QString & identity)
{
	_anonIdentity = identity;
}

void
IEEE8021x::setPassword(const QString& pwd)
{
	_password = pwd;
}

QString
IEEE8021x::getPassword(void) const
{
	return _password;
}

bool
IEEE8021x::getUseSystemCaCert(void) const
{
	return _useSystemCaCert;
}

void
IEEE8021x::setUseSystemCaCert(bool use)
{
	_useSystemCaCert = use;
}

IEEE8021x::EAP_PHASE1
IEEE8021x::getEAP(void) const
{
	return _eap;
}

void
IEEE8021x::setEAP(EAP_PHASE1 eap)
{
	_eap = eap;
}

IEEE8021x::EAP_PHASE2
IEEE8021x::getPhase2EAP(void) const
{
	return _eapPhase2;
}

void
IEEE8021x::setPhase2EAP(EAP_PHASE2 eap)
{
	_eapPhase2 = eap;
}

SettingsMap
IEEE8021x::toMap() const
{
	SettingsMap map;
	
	// EAP
	QString eap = _eapMap[(EAP)_eap];
	if (!eap.isEmpty())
	{
		QValueList<QDBusData> eap_methods;
		eap_methods.append(QDBusData::fromString(eap));
		map.insert(NM_SETTING_802_1X_EAP, QDBusData::fromQValueList(eap_methods));
	}
	// Phase2 EAP
	if (_eapPhase2 != EAP_PHASE2_AUTH_NONE)
	{
		map.insert(NM_SETTING_802_1X_PHASE2_AUTH, QDBusData::fromString(_eapMap[(EAP)_eapPhase2]));
	}

	if (!_identity.isEmpty())
		map.insert(NM_SETTING_802_1X_IDENTITY, QDBusData::fromString(_identity));

	if (!_anonIdentity.isEmpty())
		map.insert(NM_SETTING_802_1X_ANONYMOUS_IDENTITY, QDBusData::fromString(_anonIdentity));

// FIXME
/*
	if (!_caCert.isNull())
		map.insert("ca-cert", QDBusData::fromString(_caCert));
*/

	if (!_caPath.isEmpty())
		map.insert(NM_SETTING_802_1X_CA_PATH, QDBusData::fromString(_caPath));

	if (!eap.isEmpty())
		map.insert(NM_SETTING_802_1X_SYSTEM_CA_CERTS, QDBusData::fromBool(_useSystemCaCert));

//FIXME
/*
	if (!_clientCert.isNull())
		map.insert("client-cert", QDBusData::fromString(_clientCert));

	if (!_privateKey.isNull())
		map.insert("private-key", QDBusData::fromString(_privateKey));
*/

	if (!_phase1PeapVer.isNull())
		map.insert(NM_SETTING_802_1X_PHASE1_PEAPVER, QDBusData::fromString(_phase1PeapVer));

	if (!_phase1PeapLabel.isNull())
		map.insert(NM_SETTING_802_1X_PHASE1_PEAPLABEL, QDBusData::fromString(_phase1PeapLabel));

	if (!_phase1FastProvisioning.isNull())
		map.insert(NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING, QDBusData::fromString(_phase1FastProvisioning));

	if (!_phase2AuthEAP.isNull())
		map.insert(NM_SETTING_802_1X_PHASE2_AUTHEAP, QDBusData::fromString(_phase2AuthEAP));

// FIXME
/*
	if (!_phase2CaCert.isNull())
		map.insert("phase2-ca-cert", QVariant(_phase2CaCert));
*/

	if (!_phase2CaPath.isNull())
		map.insert(NM_SETTING_802_1X_PHASE2_CA_PATH, QDBusData::fromString(_phase2CaPath));

// FIXME
/*
	if (!_phase2ClientCert.isNull())
		map.insert("phase2-client-cert", QVariant(_phase2ClientCert));

	if (!_phase2PrivateKey.isNull())
		map.insert("phase2-private-key", QVariant(_phase2PrivateKey));
*/

	if(!_password.isNull())
		map.insert(NM_SETTING_802_1X_PASSWORD, QDBusData::fromString(""));
/*
 899     g_hash_table_insert (hash, "password", string_to_gvalue (self->password));
 900   if (self->pin)
 901     g_hash_table_insert (hash, "pin", string_to_gvalue (self->pin));
 902   if (self->eappsk)
 903     g_hash_table_insert (hash, "eappsk", string_to_gvalue (self->eappsk));
 904   if (self->private_key_passwd)
 905     g_hash_table_insert (hash, "private-key-passwd", string_to_gvalue (self->private_key_passwd));
 906   if (self->phase2_private_key_passwd)
 907     g_hash_table_insert (hash, "phase2-private-key-passwd", string_to_gvalue (self->phase2_private_key_passwd));
*/

	return map;
}

void
IEEE8021x::fromMap(const SettingsMap& map)
{
	kdDebug() << "IEEE8021x::fromMap" << endl;

	for (SettingsMap::ConstIterator it = map.begin(); it != map.end(); ++it)
	{
		if(it.key() == NM_SETTING_802_1X_EAP)
		{
			QValueList<QDBusData> eap_methods = it.data().toQValueList();
			if (!eap_methods.isEmpty())
			{
				QString eap = eap_methods.first().toString();
				QBiDirectionalMap<EAP, QString>::Iterator it2;
				if (_eapMap.end() != (it2 = _eapMap.findData(eap)))
					setEAP((EAP_PHASE1)it2.key());
			}
			else
			{
				// older version of NM used QString instead of QValueList<QString>
				QString eap = it.data().toString();
				if (!eap.isEmpty())
				{
					QBiDirectionalMap<EAP, QString>::Iterator it2;
					if (_eapMap.end() != (it2 = _eapMap.findData(eap)))
						setEAP((EAP_PHASE1)it2.key());
				}
			}
		}
		else if (it.key() == NM_SETTING_802_1X_PHASE2_AUTH)
		{
			QString eapPhase2 = it.data().toString();
			QBiDirectionalMap<EAP, QString>::Iterator it2;
			if (_eapMap.end() != (it2 = _eapMap.findData(eapPhase2)))
				setPhase2EAP((EAP_PHASE2)it2.key());
		}
		else if (it.key() == NM_SETTING_802_1X_IDENTITY)
			_identity = it.data().toString();
		else if (it.key() == NM_SETTING_802_1X_ANONYMOUS_IDENTITY)
			_anonIdentity = it.data().toString();
		else if (it.key() == NM_SETTING_802_1X_CA_PATH)
			_caPath	= it.data().toString();
		else if (it.key() == NM_SETTING_802_1X_PHASE1_PEAPVER)
			_phase1PeapVer = it.data().toString();
		else if (it.key() == NM_SETTING_802_1X_PHASE1_PEAPLABEL)
			_phase1PeapLabel = it.data().toString();
		else if (it.key() == NM_SETTING_802_1X_PHASE1_FAST_PROVISIONING)
			_phase1FastProvisioning = it.data().toString();
		else if (it.key() == NM_SETTING_802_1X_PHASE2_AUTHEAP)
			_phase2AuthEAP = it.data().toString();
		else if (it.key() == NM_SETTING_802_1X_PHASE2_CA_PATH)
			_phase2CaPath = it.data().toString();
		else if (it.key() == NM_SETTING_802_1X_SYSTEM_CA_CERTS)
			_useSystemCaCert = it.data().toBool();
		else
			kdWarning() << k_funcinfo << " Unknown setting: " << it.key() << endl;
	}
}

SettingsMap
IEEE8021x::toSecretsMap(bool with_settings) const
{
	SettingsMap map;
	kdDebug() << "IEEE8021x::toSecretsMap" << endl;
	// first serialize the settings if needed
	if (with_settings)
		map = toMap();

	// add password
	if (!_password.isNull())
		map.insert(NM_SETTING_802_1X_PASSWORD, QDBusData::fromString(_password));

	if (!_privateKeyPasswd.isNull())
		map.insert(NM_SETTING_802_1X_PRIVATE_KEY, QDBusData::fromString(_privateKeyPasswd));

	if (!_phase2PrivateKeyPasswd.isNull())
		map.insert(NM_SETTING_802_1X_PHASE2_PRIVATE_KEY, QDBusData::fromString(_phase2PrivateKeyPasswd));

	return map;
}

bool
IEEE8021x::fromSecretsMap(const SettingsMap& map)
{
	kdDebug() << "IEEE8021x::fromMap" << endl;

	for (SettingsMap::ConstIterator it = map.begin(); it != map.end(); ++it)
	{
		if (it.key() == NM_SETTING_802_1X_PASSWORD)
			_password = it.data().toString();
		else if (it.key() == NM_SETTING_802_1X_PRIVATE_KEY)
			_privateKeyPasswd = it.data().toString();
		else if (it.key() == NM_SETTING_802_1X_PHASE2_PRIVATE_KEY)
			_phase2PrivateKeyPasswd = it.data().toString();
		else
			kdWarning() << k_funcinfo << " Unknown setting: " << it.key() << endl;
	}
	return true;
}

bool
IEEE8021x::isValid() const
{
	return true;
}

QValueList<IEEE8021x::EAP_PHASE2>
IEEE8021x::getAllowedPhase2Methods() const
{
	QValueList<EAP_PHASE2> phase2;
	// TODO : not sure if this is correct, jsut copied from nm-applet
	switch(_eap)
	{
		case EAP_PHASE1_TTLS:
			phase2.append(EAP_PHASE2_AUTH_MSCHAPV2);
			phase2.append(EAP_PHASE2_AUTH_MSCHAP);
			phase2.append(EAP_PHASE2_AUTH_CHAP);
			phase2.append(EAP_PHASE2_AUTH_PAP);
			break;
		case EAP_PHASE1_PEAP:
			phase2.append(EAP_PHASE2_AUTH_MSCHAPV2);
			phase2.append(EAP_PHASE2_AUTH_MD5);
			break;
		default:
			phase2.append(EAP_PHASE2_AUTH_NONE);
	}
	return phase2;
}
