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

#include <tqtimer.h>

#include <tdeglobal.h>
#include <tdeconfig.h>
#include <kstaticdeleter.h>
#include <kdebug.h>

#include "knetworkmanager-storage.h"
#include "knetworkmanager-connection_store.h"
#include "knetworkmanager-wireless_connection.h"
#include "knetworkmanager-wired_connection.h"
#include "knetworkmanager-cdma_connection.h"
#include "knetworkmanager-gsm_connection.h"
#include "knetworkmanager-vpn_connection.h"
#include "knetworkmanager-connection.h"
#include "knetworkmanager-connection_setting.h"
#include "xmlmarshaller.h"
#include "knetworkmanager-connection_setting_info.h"
#include "knetworkmanager-connection_setting_wired.h"
#include "knetworkmanager-connection_setting_wireless.h"
#include "knetworkmanager-connection_setting_wireless_security.h"
#include "knetworkmanager-connection_setting_ipv4.h"

using namespace ConnectionSettings;

static KStaticDeleter<Storage> sd2;

// private stuff
class StoragePrivate
{
	public:
		StoragePrivate() {};
		~StoragePrivate() {};

		static Storage* _instance;
};

Storage* StoragePrivate::_instance = NULL;

Storage*
Storage::getInstance()
{
	if (StoragePrivate::_instance)
		return StoragePrivate::_instance;
	return sd2.setObject(StoragePrivate::_instance, new Storage());
}

Storage::Storage()
{
	d = new StoragePrivate();

	// defer the connection init a bit
	TQTimer::singleShot(0, this, TQT_SLOT(slotInit()));
}

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

void
Storage::slotInit()
{
	ConnectionStore* cstore = ConnectionStore::getInstance();

	// we want to get notified whenever a new connection is created, edited or deleted
	connect(cstore, TQT_SIGNAL(signalConnectionAdded(ConnectionSettings::Connection*)), this, TQT_SLOT(slotConnectionAdded(ConnectionSettings::Connection*)));
	connect(cstore, TQT_SIGNAL(signalConnectionRemoved(ConnectionSettings::Connection*)), this, TQT_SLOT(slotConnectionRemoved(ConnectionSettings::Connection*)));
}

void
Storage::slotConnectionAdded(Connection* con)
{
	// connection added, save it
	saveConnection(con);
	TDEGlobal::config()->sync();
}

void
Storage::slotConnectionRemoved(Connection* con)
{
	// find the appropriate connection and delete it from the storage
	deleteConnection(con);
	TDEGlobal::config()->sync();
}

Connection*
Storage::createConnectionByType(const TQString& cType)
{
	// TODO: use a factory class here
	if (cType == NM_SETTING_WIRELESS_SETTING_NAME)
		return new WirelessConnection();
	else if (cType == NM_SETTING_WIRED_SETTING_NAME)
		return new WiredConnection();
	else if(cType == NM_SETTING_CDMA_SETTING_NAME)
		return new CDMAConnection();
	else if(cType == NM_SETTING_GSM_SETTING_NAME)
		return new GSMConnection();
	else if (cType == NM_SETTING_VPN_SETTING_NAME)
		return new VPNConnection();
	else
		return NULL;
}

void
Storage::restoreConnections()
{
	kdDebug() << k_funcinfo << endl;
	// let's read all connections from the config-file and add them to the connection-store
	ConnectionStore* store = ConnectionStore::getInstance();
	TQStringList groups = TDEGlobal::config()->groupList();
	const TQStringList::Iterator end = groups.end();
	for ( TQStringList::Iterator it = groups.begin(); it != end; ++it )
	{
		if ( !(*it).startsWith( "Connection_" ) )
			continue;

		// restore that connection
		Connection* conn = NULL;
		if ( (conn = restoreConnection(*it)) != NULL)
		{
			// add the connection to the store
			store->addConnection(conn);
		}
	}

}

Connection*
Storage::restoreConnection(const TQString& grpname)
{
	Connection* conn = NULL;
	kdDebug() << k_funcinfo << " " << grpname << endl;

	// we have a connection to restore
	TDEConfigGroup grp( TDEGlobal::config(), grpname);
	TQString id = grp.readEntry("Id");
	TQString cType = grp.readEntry("Type");

	// ID is needed!
	if (id.isEmpty() || cType.isEmpty())
		return NULL;

	// create a new connection object by its type
	conn = createConnectionByType(cType);

	// check if the connection was successfully created
	if (!conn)
		return NULL;

	// set the connection ID
	conn->setID(id);

	// restore all appropriate settings
	TQStringList settings = grp.readListEntry("Settings");

	for (TQStringList::ConstIterator it = settings.begin(); it != settings.end(); ++it)
	{
		if ( !restoreSetting(conn, *it) )
		{
			// setting could not be restored -> Error
			kdDebug() << "  Connection " << id.ascii() << " could not be restored." << endl;
			kdError() << k_funcinfo << " Connection " << id << " could not be restored." << endl;
			delete conn;
			conn = NULL;
			return NULL;
		}
	}

	// restore all appropriate secrets
	TQStringList secrets = grp.readListEntry("Secrets");

	for (TQStringList::ConstIterator it = secrets.begin(); it != secrets.end(); ++it)
	{
		if ( !restoreSecrets(conn, *it) )
		{
			// setting could not be restored -> Error
			kdDebug() << "  Connection " << id.ascii() << " could not be restored." << endl;
			kdError() << k_funcinfo << " Connection " << id << " could not be restored." << endl;
			delete conn;
			conn = NULL;
			return NULL;
		}
	}

	return conn;
}

bool
Storage::restoreSetting(Connection* conn, const TQString& setting_grp_name)
{
	kdDebug() << k_funcinfo << " " << setting_grp_name << endl;
	kdDebug() << "restore setting: " << setting_grp_name.ascii() << endl;

	TDEConfigGroup setting_grp(TDEGlobal::config(), setting_grp_name);
	TQMap<TQString, TQString> config_map = TDEGlobal::config()->entryMap(setting_grp_name);
	TQString type = setting_grp.readEntry("Type");
		
	// get the appropriate setting from the connection
	ConnectionSetting* setting = conn->getSetting(type);
	if (!setting)
	{
		kdWarning() << k_funcinfo << "Connection " << conn->getID() << ": Setting " << type << " could not be restored" << endl;
		return false;
	}

	// read the SettingsMap from tdeconfig
	SettingsMap map;
	for(TQMap<TQString, TQString>::ConstIterator it = config_map.begin(); it != config_map.end(); ++it)
	{
		if (!it.key().startsWith("Value_"))
			continue;
			
		TQString key = it.key();
		// get the original name
		key.replace("Value_", "");

		TQString xmldata = it.data();
		TQT_DBusData dbusdata = XMLMarshaller::toTQT_DBusData(xmldata);

		map.insert(key, dbusdata);
	}

	// restore the setting from the generated map
	setting->fromMap(map);
	return true;
}

bool
Storage::restoreSecrets(Connection* conn, const TQString& secrets_grp_name)
{
	kdDebug() << k_funcinfo << " " << secrets_grp_name << endl;
	kdDebug() << "restore secret: " << secrets_grp_name.ascii() << endl;

	TDEConfigGroup secrets_grp(TDEGlobal::config(), secrets_grp_name);
	TQMap<TQString, TQString> config_map = TDEGlobal::config()->entryMap(secrets_grp_name);
	TQString type = secrets_grp.readEntry("Type");
		
	// get the appropriate setting from the connection
	ConnectionSetting* setting = conn->getSetting(type);
	if (!setting)
	{
		kdWarning() << k_funcinfo << "Connection " << conn->getID() << ": Secrets for setting " << type << " could not be restored" << endl;
		return false;
	}

	// read the SettingsMap from tdeconfig
	SettingsMap map;
	for(TQMap<TQString, TQString>::ConstIterator it = config_map.begin(); it != config_map.end(); ++it)
	{
		if (!it.key().startsWith("Value_"))
			continue;
			
		TQString key = it.key();
		// get the original name
		key.replace("Value_", "");

		TQString xmldata = it.data();
		TQT_DBusData dbusdata = XMLMarshaller::toTQT_DBusData(xmldata);

		map.insert(key, dbusdata);
	}

	// restore the setting from the generated map
	setting->fromSecretsMap(map);
	return true;
}

bool
Storage::restoreVPNSecrets(Connection* conn, const TQString& secrets_grp_name)
{
	kdDebug() << k_funcinfo << " " << secrets_grp_name << endl;
	kdDebug() << "restore secret: " << secrets_grp_name.ascii() << endl;

	TDEConfigGroup secrets_grp(TDEGlobal::config(), secrets_grp_name);
	TQMap<TQString, TQString> config_map = TDEGlobal::config()->entryMap(secrets_grp_name);
	TQString type = secrets_grp.readEntry("Type");
		
	// get the appropriate setting from the connection
	ConnectionSetting* setting = conn->getSetting(type);
	if (!setting)
	{
		kdWarning() << k_funcinfo << "Connection " << conn->getID() << ": Secrets for setting " << type << " could not be restored" << endl;
		return false;
	}

	// read the SettingsMap from tdeconfig
	SettingsMap map;
	for(TQMap<TQString, TQString>::ConstIterator it = config_map.begin(); it != config_map.end(); ++it)
	{
		if (!it.key().startsWith("Value_"))
			continue;
			
		TQString key = it.key();
		// get the original name
		key.replace("Value_", "");

		TQString xmldata = it.data();
		TQT_DBusData dbusdata = XMLMarshaller::toTQT_DBusData(xmldata);

		map.insert(key, dbusdata);
	}

	// restore the setting from the generated map
	setting->fromSecretsMap(map);
	return true;
}

void
Storage::saveConnections()
{
	kdDebug() << k_funcinfo << endl;
	kdDebug() << "Storage::saveConnections" << endl;
	printf("Storage::saveConnections\n");
	// write all connections we get from the connection-store to disk
	ConnectionStore* store = ConnectionStore::getInstance();
	TQValueList<ConnectionSettings::Connection*> connections = store->getConnections();

	for (TQValueList<ConnectionSettings::Connection*>::ConstIterator it = connections.begin(); it != connections.end(); ++it)
	{
		// save this connection
		saveConnection(*it);
	}
	TDEGlobal::config()->sync();
}

bool
Storage::saveConnection(Connection* conn)
{
	TDEConfig* config = TDEGlobal::config();
	TQString id = conn->getID();
	TQString cType = conn->getType();

	kdDebug() << k_funcinfo << " <" << id << ">" << endl;
	kdDebug() << "Storage::saveConnection " << id.ascii() << endl;

	// connections without id are evil
	if (id.isEmpty() || cType.isEmpty())
		return false;

	// let's get the config group for this connection
	TDEConfigGroup grp(config, TQString("Connection_%1").arg(id));
	TQStringList settings_grps;
	TQStringList secrets_grps;

	// save the connections settings to the configfile
	if (saveConnectionSettings(conn, settings_grps, secrets_grps))
	{
		grp.writeEntry("Type", cType);
		grp.writeEntry("Id", id);
		// save the list of settings groups
		grp.writeEntry("Settings", settings_grps);
		grp.writeEntry("Secrets", secrets_grps);
	}
	return false;
}

bool
Storage::saveConnectionSettings(Connection* conn, TQStringList& settings_grps, TQStringList& secrets_grps)
{
	TQString id = conn->getID();

	// connections without id are evil
	if (id.isEmpty())
		return false;

	// iterate over all settings
	TQValueList<ConnectionSetting*> settings = conn->getSettings();
	TQString setting_grp;
	TQString secrets_grp;

	// save all settings
	for (TQValueList<ConnectionSetting*>::ConstIterator it = settings.begin(); it != settings.end(); ++it)
	{
		if (!saveConnectionSetting(conn, *it, setting_grp))
			return false;

		if ((*it)->hasSecrets())
		{
			if (!saveConnectionSecrets(conn, *it, secrets_grp))
				return false;
			secrets_grps.append(secrets_grp);
		}

		settings_grps.append(setting_grp);
	}

	return true;
}

bool
Storage::saveConnectionSetting(Connection* conn, ConnectionSetting* setting, TQString& setting_grp)
{
	TDEConfig* config = TDEGlobal::config();
	TQString id = conn->getID();
	TQString type = setting->getType();

	kdDebug() << k_funcinfo << " <" << id << "> <" << type << ">" << endl;

	// ID is necessary
	if (id.isEmpty())
		return false;

	// get a group for this setting
	setting_grp = TQString("ConnectionSetting_%1_%2").arg(id).arg(type);
	TDEConfigGroup grp(config, setting_grp);

	// write the type
	grp.writeEntry("Type", type);

	// write the values
	SettingsMap map = setting->toMap();
	for (SettingsMap::ConstIterator it = map.begin(); it != map.end(); ++it)
	{
		kdDebug() << k_funcinfo << "  " << TQString("Value_%1").arg(it.key()) << " = " << XMLMarshaller::fromTQT_DBusData( it.data() )<< endl;
		grp.writeEntry(TQString("Value_%1").arg(it.key()), XMLMarshaller::fromTQT_DBusData( it.data() ));
	}
	return true;
}

bool
Storage::saveConnectionSecrets(Connection* conn, ConnectionSetting* setting, TQString& setting_grp)
{
	TDEConfig* config = TDEGlobal::config();
	TQString id = conn->getID();
	TQString type = setting->getType();
	bool storage_requested;

	kdDebug() << k_funcinfo << " <" << id << "> <" << type << ">" << endl;

	// ID is necessary
	if (id.isEmpty())
		return false;

	// see if permanent storage was requested by the user
	SettingsMap setting_map = setting->toMap();
	storage_requested = true;
	for (SettingsMap::ConstIterator it = setting_map.begin(); it != setting_map.end(); ++it)
	{
		if (it.key() == "Commit to disk") {
			if (XMLMarshaller::fromTQT_DBusData(it.data()) == TQString("true")) {
				storage_requested = true;
			}
			if (XMLMarshaller::fromTQT_DBusData(it.data()) == TQString("false")) {
				storage_requested = false;
			}
		}
	}
	printf("Secrets storage requested: %d\n", storage_requested);

	// get a group for this setting
	setting_grp = TQString("ConnectionSecrets_%1_%2").arg(id).arg(type);
	TDEConfigGroup grp(config, setting_grp);

	// write the type
	grp.writeEntry("Type", type);

	// write the values
	SettingsMap map = setting->toSecretsMap(false);
	for (SettingsMap::ConstIterator it = map.begin(); it != map.end(); ++it)
	{
		kdDebug() << k_funcinfo << "  " << TQString("Value_%1").arg(it.key()) << " = " << XMLMarshaller::fromTQT_DBusData( it.data() )<< endl;
		if (storage_requested == true) {
			grp.writeEntry(TQString("Value_%1").arg(it.key()), XMLMarshaller::fromTQT_DBusData( it.data() ));
		}
		else {
			grp.writeEntry(TQString("Value_%1").arg(it.key()), TQString("") );
		}
	}
	return true;
}

bool
Storage::hasSecretsStored(Connection* connection)
{
	TQString id = connection->getID();

	// ID is necessary
	if (id.isEmpty())
		return false;

	TQValueList<ConnectionSetting*> settings = connection->getSettings();
	for (TQValueList<ConnectionSetting*>::Iterator it = settings.begin(); it != settings.end(); ++it)
	{
		if (hasSecretsStored(connection, *it))
			return true;
	}
	return false;
}


bool
Storage::hasSecretsStored(Connection* connection, ConnectionSetting* setting)
{
	TQString id = connection->getID();
	TQString type = setting->getType();

	kdDebug() << "Storage::hasSecretsStored" << endl;

	// ID is necessary
	if (id.isEmpty())
		return false;

	// get a group for this setting
	TQString setting_grp_name = TQString("ConnectionSecrets_%1_%2").arg(id).arg(type);

	TQMap<TQString, TQString> config_map = TDEGlobal::config()->entryMap(setting_grp_name);

	return !(config_map.isEmpty());
}

bool
Storage::restoreAllSecrets(Connection* connection)
{
	TQString id = connection->getID();
	bool retval = true;

	if (id.isEmpty())
		return false;

	TQValueList<ConnectionSetting*> settings = connection->getSettings();
	for (TQValueList<ConnectionSetting*>::Iterator it = settings.begin(); it != settings.end(); ++it)
	{
		if (hasSecretsStored(connection, *it))
			if (!restoreSecrets(connection, *it))
				retval = false;
	}
	return retval;
}

bool
Storage::restoreSecrets(Connection* connection, ConnectionSetting* setting)
{
	TQString id = connection->getID();
	TQString type = setting->getType();

	kdDebug() << "Storage::restoreSecrets" << endl;
	// ID is necessary
	if (id.isEmpty())
		return false;

	// get a group for this setting
	TQString setting_grp = TQString("ConnectionSecrets_%1_%2").arg(id).arg(type);

	// restore the setting
	return restoreSecrets(connection, setting_grp);
}

bool
Storage::restoreVPNSecrets(Connection* connection, ConnectionSetting* setting)
{
	TQString id = connection->getID();
	TQString type = setting->getType();

	printf("Storage::restoreVPNSecrets\n");
	kdDebug() << "Storage::restoreVPNSecrets" << endl;
	// ID is necessary
	if (id.isEmpty())
		return false;

	// get a group for this setting
	TQString setting_grp = TQString("ConnectionSecrets_%1_%2").arg(id).arg(type);

	// restore the setting
	return restoreVPNSecrets(connection, setting_grp);
}

bool
Storage::deleteConnection(Connection* conn)
{
	TDEConfig* config = TDEGlobal::config();
	TQString id = conn->getID();
	TQString cType = conn->getType();

	kdDebug() << k_funcinfo << " <" << id << ">" << endl;
	kdDebug() << "Storage::deleteConnection " << id.ascii() << endl;

	// connections without id are evil
	if (id.isEmpty() || cType.isEmpty())
		return false;

	// let's get the config group for this connection
	TDEConfigGroup grp(config, TQString("Connection_%1").arg(id));


	// delete all associated settings
	TQStringList settings = grp.readListEntry("Settings");

	for (TQStringList::ConstIterator it = settings.begin(); it != settings.end(); ++it)
	{
		TDEConfigGroup setting(config, *it);
		setting.deleteGroup();
	}

	// delete all associated secrets
	TQStringList secrets = grp.readListEntry("Secrets");

	for (TQStringList::ConstIterator it = secrets.begin(); it != secrets.end(); ++it)
	{
		TDEConfigGroup setting(config, *it);
		setting.deleteGroup();
	}

	grp.deleteGroup();

	return true;
}

#include "knetworkmanager-storage.moc"
