/**************************************************************************
 *
 * knetworkmanager-connection_setting_wireless_security.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_wireless_security.h"
#include "knetworkmanager-accesspoint.h"
#include "knetworkmanager-connection_setting_wireless.h"
#include "knetworkmanager-connection.h"
#include "sha1.h"
#include "md5.h"


using namespace ConnectionSettings;

/*
	class WirelessSecurity
*/
WirelessSecurity::WirelessSecurity(Connection* conn)
	: ConnectionSetting(conn, NM_SETTING_WIRELESS_SECURITY_SETTING_NAME)
	, _keyMgmt(KEY_MGMT_NONE)
	, _wepTxKeyidx(0)
	, _authAlg(AUTH_ALG_NONE)
	, _proto(PROTO_NONE)
	, _pairwise(CIPHER_TKIP | CIPHER_CCMP)
	, _group(CIPHER_TKIP | CIPHER_CCMP)
{
	// init key_mgmt map
	_keyMgmtMap[KEY_MGMT_NONE] = "none";
	_keyMgmtMap[KEY_MGMT_WPA_PSK] = "wpa-psk";
	_keyMgmtMap[KEY_MGMT_WPA_EAP] = "wpa-eap";
	_keyMgmtMap[KEY_MGMT_IEEE8021X] = "ieee8021x";

	// init auth_alg map
	_authAlgMap[AUTH_ALG_NONE] = QString::null; // do not include auth alg if none is needed
	_authAlgMap[AUTH_ALG_OPEN] = "open";
	_authAlgMap[AUTH_ALG_SHARED] = "shared";
	_authAlgMap[AUTH_ALG_LEAP] = "leap";

	// proto map
	_protoMap[PROTO_WPA] = "wpa";
	_protoMap[PROTO_RSN] = "rsn"; // Info: rsn is wpa2

	// cipher map
	_cipherMap[CIPHER_TKIP] = "tkip";
	_cipherMap[CIPHER_CCMP] = "ccmp";
	_cipherMap[CIPHER_WEP40] = "wep40";
	_cipherMap[CIPHER_WEP104] = "wep104";

}

bool WirelessSecurity::getEnabled() const
{
	Connection* conn = getConnection();
	if (conn)
	{
		Wireless* wireless = dynamic_cast<Wireless*>(conn->getSetting(NM_SETTING_WIRELESS_SETTING_NAME));
		if (wireless && wireless->getSecurity() != getType())
		{
			kdDebug() << "WirelessSecurity::getEnabled false " << wireless->getSecurity().ascii() << endl;
			return false;
		}
	}
	return true;
}

int
WirelessSecurity::getWepTxidx(void) const
{
	return _wepTxKeyidx;
}

void
WirelessSecurity::setWepTxidx(int idx)
{
	if (idx >= 0 && idx < 4)
		_wepTxKeyidx = idx;
}

void
WirelessSecurity::setWepKey(int idx, QString key)
{
	kdDebug() << "WirelessSecurity::setWepKey " << idx << " " << key.ascii() << endl;
	if (idx >= 0 && idx < 4)
		_wepKey[idx] = key;
}

QString
WirelessSecurity::getWepKey(int idx) const
{
	if (idx >= 0 && idx < 4)
		return _wepKey[idx];
	return QString::null;
}

uint32_t
WirelessSecurity::getGroupCiphers(void) const
{
	return _group;
}

void
WirelessSecurity::setGroupCiphers(uint32_t ciphers)
{
	_group= ciphers & (CIPHER_TKIP | CIPHER_CCMP | CIPHER_WEP40 | CIPHER_WEP104);
}

uint32_t
WirelessSecurity::getPairwiseCiphers(void) const
{
	return _pairwise;
}

void
WirelessSecurity::setPairwiseCiphers(uint32_t ciphers)
{
	// only tkip and ccmp allowed
	_pairwise = ciphers & (CIPHER_TKIP | CIPHER_CCMP);
}

uint32_t
WirelessSecurity::getProto(void) const
{
	return _proto;
}

void
WirelessSecurity::setProto(uint32_t proto)
{
	_proto = proto & (PROTO_WPA | PROTO_RSN);
}

void
WirelessSecurity::addProto(uint32_t proto)
{
	setProto(_proto | proto);
}

void
WirelessSecurity::delProto(uint32_t proto)
{
	setProto(_proto & ~proto);
}

QString
WirelessSecurity::getPSK(void) const
{
	return _psk;
}

void
WirelessSecurity::setPSK(const QString& psk)
{
	_psk = psk;
}

QString
WirelessSecurity::getLeapPassword(void) const
{
	return _leapPassword;
}

void
WirelessSecurity::setLeapPassword(const QString& p)
{
	_leapPassword = p;
}

QString
WirelessSecurity::getLeapUsername(void) const
{
	return _leapUsername;
}

void
WirelessSecurity::setLeapUsername(const QString& u)
{
	_leapUsername = u;
}

WirelessSecurity::KEY_MGMT
WirelessSecurity::getKeyMgmt(void) const
{
	return _keyMgmt;
}

void
WirelessSecurity::setKeyMgmt(KEY_MGMT keyMgmt)
{
	_keyMgmt = keyMgmt;
}

WirelessSecurity::AUTH_ALG
WirelessSecurity::getAuthAlg(void) const
{
	return _authAlg;
}

void
WirelessSecurity::setAuthAlg(AUTH_ALG authAlg)
{
	_authAlg = authAlg;
}

SettingsMap
WirelessSecurity::toMap() const
{
	SettingsMap map;

	// KEY MGMT
	map.insert(NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, QDBusData::fromString(_keyMgmtMap[_keyMgmt]));

	// WEP TX KEYIDX is only needed if WEP is used
	if (_keyMgmt == KEY_MGMT_NONE && _wepTxKeyidx >= 0 && _wepTxKeyidx <= 3)
	{
		// WEP TX KEYIDX
		map.insert(NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, QDBusData::fromInt32(_wepTxKeyidx));
	}

	// AUTH ALG
	if (_authAlg != AUTH_ALG_NONE)
		map.insert(NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, QDBusData::fromString(_authAlgMap[_authAlg]));

	// PROTO is only needed for WPA connections 
	if (_proto != PROTO_NONE && (_keyMgmt == KEY_MGMT_WPA_NONE || _keyMgmt == KEY_MGMT_WPA_PSK || _keyMgmt == KEY_MGMT_WPA_EAP))
	{
		QValueList<QDBusData> protos;
		for (QMap<PROTO, QString>::ConstIterator it = _protoMap.begin(); it != _protoMap.end(); ++it)
			if (_proto & it.key())
				protos.append(QDBusData::fromString(it.data()));
		if (protos.size() > 0)
			map.insert(NM_SETTING_WIRELESS_SECURITY_PROTO, QDBusData::fromQValueList(protos));
	}

	// groupcipher
	if (_group != CIPHER_NONE)
	{
		QValueList<QDBusData> ciphers;
		for (QMap<CIPHERS, QString>::ConstIterator it = _cipherMap.begin(); it != _cipherMap.end(); ++it)
			if (_group & it.key())
				ciphers.append(QDBusData::fromString(it.data()));

		if (ciphers.size() > 0)
			map.insert(NM_SETTING_WIRELESS_SECURITY_GROUP, QDBusData::fromQValueList(ciphers));
	}

	// pairwise cipher
	if (_pairwise != CIPHER_NONE)
	{
		QValueList<QDBusData> ciphers;

		for (QMap<CIPHERS, QString>::ConstIterator it = _cipherMap.begin(); it != _cipherMap.end(); ++it)
			if (_pairwise & it.key())
				ciphers.append(QDBusData::fromString(it.data()));

		if (ciphers.size() > 0)
			map.insert(NM_SETTING_WIRELESS_SECURITY_PAIRWISE, QDBusData::fromQValueList(ciphers));
	}

	if (!_psk.isNull())
		map.insert(NM_SETTING_WIRELESS_SECURITY_PSK, QDBusData::fromString(""));

	if (!_leapUsername.isEmpty())
		map.insert(NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME, QDBusData::fromString(_leapUsername));

	if (!_leapPassword.isEmpty())
		map.insert(NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, QDBusData::fromString(""));
	return map;
}

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

	for (SettingsMap::ConstIterator it = map.begin(); it != map.end(); ++it)
	{
		if (it.key() == NM_SETTING_WIRELESS_SECURITY_KEY_MGMT)
		{
			QBiDirectionalMap<KEY_MGMT, QString>::Iterator it2;
			if (_keyMgmtMap.end() != (it2 = _keyMgmtMap.findData(it.data().toString())))
				setKeyMgmt(it2.key());
		}
		else if (it.key() == NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX)
		{
			_wepTxKeyidx = it.data().toInt32();
		}
		else if (it.key() == NM_SETTING_WIRELESS_SECURITY_AUTH_ALG)
		{
			QBiDirectionalMap<AUTH_ALG, QString>::Iterator it2;
			if (_authAlgMap.end() != (it2 = _authAlgMap.findData(it.data().toString())))
				setAuthAlg(it2.key());
		}
		else if(it.key() == NM_SETTING_WIRELESS_SECURITY_PROTO)
		{
			QValueList<QDBusData> proto_list = it.data().toQValueList();
			uint32_t protos = PROTO_NONE;
			for (QValueList<QDBusData>::Iterator proto_it = proto_list.begin(); proto_it != proto_list.end(); ++proto_it)
			{
				QBiDirectionalMap<PROTO, QString>::Iterator it2;
				if (_protoMap.end() != (it2 = _protoMap.findData( (*proto_it).toString() )))
					protos |= it2.key();
			}
			setProto(protos);
		}
		else if(it.key() == NM_SETTING_WIRELESS_SECURITY_GROUP)
		{
			QValueList<QDBusData> cipher_list = it.data().toQValueList();
			uint32_t ciphers = CIPHER_NONE;
			for (QValueList<QDBusData>::Iterator cipher_it = cipher_list.begin(); cipher_it != cipher_list.end(); ++cipher_it)
			{
				QBiDirectionalMap<CIPHERS, QString>::Iterator it2;
				if (_cipherMap.end() != (it2 = _cipherMap.findData( (*cipher_it).toString() )))
					ciphers |= it2.key();
			}
			setGroupCiphers(ciphers);
		}
		else if (it.key() == NM_SETTING_WIRELESS_SECURITY_PAIRWISE)
		{
			QValueList<QDBusData> cipher_list = it.data().toQValueList();
			uint32_t ciphers = CIPHER_NONE;
			for (QValueList<QDBusData>::Iterator cipher_it = cipher_list.begin(); cipher_it != cipher_list.end(); ++cipher_it)
			{
				QBiDirectionalMap<CIPHERS, QString>::Iterator it2;
				if (_cipherMap.end() != (it2 = _cipherMap.findData( (*cipher_it).toString() )))
					ciphers |= it2.key();
			}
			setPairwiseCiphers(ciphers);
		}
		else if (it.key() == NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME)
		{
			_leapUsername = it.data().toString();
		}
		else if (it.key() == NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD)
		{
			_leapPassword = it.data().toString();
		}
		else
			kdWarning() << k_funcinfo << " Unknown setting: " << it.key() << endl;
	}
}

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

	// add the hashed psk
	if (!_psk.isNull())
	{
		map.insert(NM_SETTING_WIRELESS_SECURITY_PSK, QDBusData::fromString(_psk), TRUE);
	}

	// wep keys
	if (!_wepKey[0].isNull())
	{
		kdDebug() << "insert wep key0: " << _wepKey[0].ascii() << endl;
		map.insert(NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, QDBusData::fromString(_wepKey[0]));
	}

	if (!_wepKey[1].isNull())
	{
		map.insert(NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, QDBusData::fromString(_wepKey[1]));
	}

	if (!_wepKey[2].isNull())
	{
		map.insert(NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, QDBusData::fromString(_wepKey[2]));
	}

	if (!_wepKey[3].isNull())
	{
		map.insert(NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, QDBusData::fromString(_wepKey[3]));
	}

	if (!_leapPassword.isEmpty())
		map.insert(NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, QDBusData::fromString(_leapPassword));

	return map;
}

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

	for (SettingsMap::ConstIterator it = map.begin(); it != map.end(); ++it)
	{
		if (it.key() == NM_SETTING_WIRELESS_SECURITY_PSK)
		{
			_psk = it.data().toString();
		}
		else if (it.key() == NM_SETTING_WIRELESS_SECURITY_WEP_KEY0)
		{
			_wepKey[0] = it.data().toString();
		}
		else if (it.key() == NM_SETTING_WIRELESS_SECURITY_WEP_KEY1)
		{
			_wepKey[1] = it.data().toString();
		}
		else if (it.key() == NM_SETTING_WIRELESS_SECURITY_WEP_KEY2)
		{
			_wepKey[2] = it.data().toString();
		}
		else if (it.key() == NM_SETTING_WIRELESS_SECURITY_WEP_KEY3)
		{
			_wepKey[3] = it.data().toString();
		}
		else
			kdWarning() << k_funcinfo << " Unknown setting: " << it.key() << endl;
	}
	return true;
}

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