/*
 * libretroshare/src/pqi: p3cfgmgr.cc
 *
 * 3P/PQI network interface for RetroShare.
 *
 * Copyright 2007-2008 by Robert Fernie.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License Version 2 as published by the Free Software Foundation.
 *
 * 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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.
 *
 * Please report all bugs and problems to "retroshare@lunamutt.com".
 *
 */

#include "util/rsdir.h"
//#include "retroshare/rspeers.h"
#include "pqi/p3cfgmgr.h"
#include "pqi/authssl.h"
#include "pqi/pqibin.h"
#include "pqi/pqistore.h"
#include "pqi/pqiarchive.h"
#include <errno.h>
#include <rsserver/p3face.h>
#include <util/rsdiscspace.h>
#include "util/rsstring.h"

#include "serialiser/rsconfigitems.h"

/*
#define CONFIG_DEBUG 1
*/
#define BACKEDUP_SAVE


p3ConfigMgr::p3ConfigMgr(std::string dir, std::string fname, std::string signame)
        :basedir(dir), metafname(fname), metasigfname(signame), cfgMtx("p3ConfigMgr"),
	mConfigSaveActive(true)
{
	oldConfigType = checkForGlobalSigConfig();

	// configuration to load correct global types
	pqiConfig::globalConfigType = oldConfigType;

}

void	p3ConfigMgr::tick()
{
	bool toSave = false;

      {
	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/

	/* iterate through and check if any have changed */
	std::map<uint32_t, pqiConfig *>::iterator it;
	for(it = configs.begin(); it != configs.end(); it++)
	{
		if (it->second->HasConfigChanged(0))
		{

#ifdef CONFIG_DEBUG
			std::cerr << "p3ConfigMgr::tick() Config Changed - Element: ";
			std::cerr << it->first;
			std::cerr << std::endl;
#endif

			toSave = true;
		}
	}

	/* disable saving before exit */
	if (!mConfigSaveActive)
	{
		toSave = false;
	}
      }

	if (toSave)
	{
		saveConfiguration();
	}
}


void	p3ConfigMgr::saveConfiguration()
{
	if(!RsDiscSpace::checkForDiscSpace(RS_CONFIG_DIRECTORY))
		return ;

	saveConfig();


}

void p3ConfigMgr::saveConfig()
{

	bool ok= true;

	RsStackMutex stack(cfgMtx);  /***** LOCK STACK MUTEX ****/

	std::map<uint32_t, pqiConfig *>::iterator it;
	for(it = configs.begin(); it != configs.end(); it++)
	{
		if (it->second->HasConfigChanged(1))
		{
#ifdef CONFIG_DEBUG
			std::cerr << "p3ConfigMgr::globalSaveConfig() Saving Element: ";
			std::cerr << it->first;
			std::cerr << std::endl;
#endif
			ok &= it->second->saveConfiguration();
		}
		/* save metaconfig */
	}

	if(ok && oldConfigType)
		removeOldConfigType();
	return;
}


void p3ConfigMgr::removeOldConfigType()
{
	std::string fName = basedir + "/" + metafname;
	std::string sigfName = basedir + "/" + metasigfname;

	remove(fName.c_str());
	remove(sigfName.c_str());

	//now set globalconfig type to false so mgr saves
	oldConfigType = false;
	pqiConfig::globalConfigType = oldConfigType;

}
void p3ConfigMgr::globalSaveConfig()
{

	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/

#ifdef CONFIG_DEBUG
	std::cerr << "p3ConfigMgr::globalSaveConfig()";
	std::cerr << std::endl;
#endif

	RsConfigKeyValueSet *item = new RsConfigKeyValueSet();

	std::map<uint32_t, pqiConfig *>::iterator it;
	for(it = configs.begin(); it != configs.end(); it++)
	{
		if (it->second->HasConfigChanged(1))
		{
#ifdef CONFIG_DEBUG
			std::cerr << "p3ConfigMgr::globalSaveConfig() Saving Element: ";
			std::cerr << it->first;
			std::cerr << std::endl;
#endif
			it->second->saveConfiguration();
		}
		/* save metaconfig */

#ifdef CONFIG_DEBUG
		std::cerr << "p3ConfigMgr::globalSaveConfig() Element: ";
		std::cerr << it->first << " Hash: " << it->second->Hash();
		std::cerr << std::endl;
#endif
		if (it->second->Hash() == "")
		{
			/* skip if no hash */
			continue;
		}

		RsTlvKeyValue kv;
		rs_sprintf(kv.key, "%lu", it->first);
		kv.value = it->second->Hash();
		item->tlvkvs.pairs.push_back(kv);
	}

#ifdef CONFIG_DEBUG
	std::cerr << "p3ConfigMgr::globalSaveConfig() Complete MetaConfigItem: ";
	std::cerr << std::endl;
	item->print(std::cerr, 20);

#endif
	/* construct filename */
	std::string fname = basedir;
	std::string sign_fname = basedir;
	std::string fname_backup, sign_fname_backup; // back up files

	if (basedir != "")
	{
		fname += "/";
		sign_fname += "/";
	}

	fname += metafname;
	sign_fname += metasigfname;
	fname_backup = fname + ".tmp";
	sign_fname_backup = sign_fname + ".tmp";

	/* Write the data to a stream */
	uint32_t bioflags = BIN_FLAGS_WRITEABLE;
	BinMemInterface *configbio = new BinMemInterface(1000, bioflags);
	RsSerialiser *rss = new RsSerialiser();
	rss->addSerialType(new RsGeneralConfigSerialiser());
	pqistore store(rss, "CONFIG", configbio, BIN_FLAGS_WRITEABLE);

	store.SendItem(item);

	/* sign data */
	std::string signature;
	AuthSSL::getAuthSSL()->SignData(configbio->memptr(), configbio->memsize(), signature);

    /* write signature to configuration */
    BinMemInterface *signbio = new BinMemInterface(signature.c_str(),
    		signature.length(), BIN_FLAGS_READABLE);

#ifdef CONFIG_DEBUG
	std::cerr << "p3ConfigMgr::globalSaveConfig() MetaFile Signature:";
	std::cerr << std::endl;
	std::cerr << signature;
	std::cerr << std::endl;
#endif

	// begin two pass save
	backedUpFileSave(fname, fname_backup, sign_fname, sign_fname_backup, configbio, signbio);

	delete signbio;

}

bool p3ConfigMgr::backedUpFileSave(const std::string& fname, const std::string& fname_backup, const std::string& sign_fname,
		const std::string& sign_fname_backup, BinMemInterface* configbio, BinMemInterface* signbio){

	FILE *file = NULL, *sign = NULL;
	char *config_buff=NULL, *sign_buff=NULL;
	int size_file=0, size_sign=0;

	// begin two pass saving by writing to back up file instead
	if (!configbio->writetofile(fname_backup.c_str()) || !signbio->writetofile(sign_fname_backup.c_str()))
	{
#ifdef CONFIG_DEBUG
		std::cerr << "p3ConfigMgr::backedupFileSave() Failed write to Backup MetaFiles " << fname_backup
				<< " and " << sign_fname_backup;
		std::cerr << std::endl;
#endif
		return false;
	}

#ifdef CONFIG_DEBUG
	std::cerr << "p3Config::backedUpFileSave() Save file and keeps a back up " << std::endl;
#endif

	// open file from which to collect buffer
	file = RsDirUtil::rs_fopen(fname.c_str(), "rb");
	sign = RsDirUtil::rs_fopen(sign_fname.c_str(), "rb");

	// if failed then create files
	if((file == NULL) || (sign == NULL)){
#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::backedUpFileSave()  failed to open meta files " << fname << std::endl;
#endif

		file = RsDirUtil::rs_fopen(fname.c_str(), "wb");
		sign = RsDirUtil::rs_fopen(sign_fname.c_str(), "wb");

		if((file == NULL) || (sign == NULL)){
			std::cerr << "p3Config::backedUpFileSave()  failed to open backup meta files" << fname_backup << std::endl;
			return false;
		}
	}


	//determine file size
	fseek(file, 0L, SEEK_END);
	size_file = ftell(file);
	fseek(file, 0L, SEEK_SET);

	fseek(sign, 0L, SEEK_END);
	size_sign = ftell(sign);
	fseek(sign, 0L, SEEK_SET);

	if((size_file) > 0 && (size_sign > 0)){
		//read this into a buffer
		config_buff = new char[size_file];
		fread(config_buff, 1, size_file, file);

		//read this into a buffer
		sign_buff = new char[size_sign];
		fread(sign_buff, 1, size_sign, sign);
	}

	fclose(file);
	fclose(sign);

	// rename back-up to current file
	if(!RsDirUtil::renameFile(fname_backup, fname)  || !RsDirUtil::renameFile(sign_fname_backup, sign_fname)){
#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::backedUpFileSave() Failed to rename backup meta files: " << std::endl
				<< fname_backup << " to " << fname << std::endl
				<< sign_fname_backup << " to " << sign_fname << std::endl;
#endif
		if (config_buff)
			delete[] config_buff;
		if (sign_buff)
			delete[] sign_buff;

		return true;
	}

	if((size_file) > 0 && (size_sign > 0)){

		// now write actual back-up file
		file = RsDirUtil::rs_fopen(fname_backup.c_str(), "wb");
		sign = RsDirUtil::rs_fopen(sign_fname_backup.c_str(), "wb");

		if((file == NULL) || (sign == NULL)){
#ifdef CONFIG_DEBUG
			std::cerr << "p3Config::backedUpFileSave()  fopen failed for file: " << fname_backup << std::endl;
#endif
			delete[] config_buff;
			delete[] sign_buff;

			return true;
		}

		if(size_file != (int) fwrite(config_buff,1,  size_file, file))
			RsServer::notify()->AddSysMessage(0, RS_SYS_WARNING, "Write error", "Error while writing backup configuration file " + fname_backup + "\nIs your disc full or out of quota ?");

		if(size_sign != (int) fwrite(sign_buff, 1, size_sign, sign))
			RsServer::notify()->AddSysMessage(0, RS_SYS_WARNING, "Write error", "Error while writing main signature file " + sign_fname_backup + "\nIs your disc full or out of quota ?");
		
		fclose(file);
		fclose(sign);

#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::backedUpFileSave() finished backed up save.  " << std::endl;
#endif

		delete[] config_buff;
		delete[] sign_buff;
	}
    return true;
}

void p3ConfigMgr::loadConfiguration()
{
	if(oldConfigType)
		globalLoadConfig();
	else
		loadConfig();

	return;
}

void p3ConfigMgr::loadConfig()
{
	std::map<uint32_t, pqiConfig *>::iterator cit;
	std::string dummyHash = "dummyHash";
	for (cit = configs.begin(); cit != configs.end(); cit++)
	{
#ifdef CONFIG_DEBUG
		std::cerr << "p3ConfigMgr::loadConfig() Element: ";
		std::cerr << cit->first <<"Dummy Hash: " << dummyHash;
		std::cerr << std::endl;
#endif

		cit->second->loadConfiguration(dummyHash);

		/* force config to NOT CHANGED */
		cit->second->HasConfigChanged(0);
		cit->second->HasConfigChanged(1);
	}

	return;
}

void	p3ConfigMgr::globalLoadConfig()
{
	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/

#ifdef CONFIG_DEBUG
	std::cerr << "p3ConfigMgr::loadConfiguration()";
	std::cerr << std::endl;
#endif

	/* construct filename */
	std::string fname = basedir;
	std::string sign_fname = basedir;
	std::string fname_backup, sign_fname_backup;

	if (basedir != "")
	{
		fname += "/";
		sign_fname += "/";
	}

	fname += metafname;
	sign_fname += metasigfname;

	// temporary files
	fname_backup = fname + ".tmp";
	sign_fname_backup = sign_fname + ".tmp";

	BinMemInterface* membio = new BinMemInterface(1000, BIN_FLAGS_READABLE);

	// Will attempt to get signature first from meta file then if that fails try temporary meta files, these will correspond to temp configs
	bool pass = getSignAttempt(fname, sign_fname, membio);

	// if first attempt fails then try and temporary files
	if(!pass){

		#ifdef CONFIG_DEBUG
		std::cerr << "\np3ConfigMgr::loadConfiguration(): Trying to load METACONFIG item and METASIGN with temporary files";
		std::cerr << std::endl;
		#endif

		pass = getSignAttempt(fname_backup, sign_fname_backup , membio);

		if(!pass){
			#ifdef CONFIG_DEBUG
			std::cerr << "\np3ConfigMgr::loadConfiguration(): failed to load METACONFIG item and METASIGN";
			std::cerr << std::endl;
			#endif

			return;
		}
	}

	membio->fseek(0); /* go back to start of file */

	RsSerialiser *rss = new RsSerialiser();
	rss->addSerialType(new RsGeneralConfigSerialiser());
	pqistore stream(rss, "CONFIG", membio, BIN_FLAGS_READABLE);

	RsItem *rsitem = stream.GetItem();

	RsConfigKeyValueSet *item = dynamic_cast<RsConfigKeyValueSet *>(rsitem);
	if (!item)
	{
		delete rsitem;
		return;
	}

#ifdef CONFIG_DEBUG
	std::cerr << "p3ConfigMgr::loadConfiguration() Loaded MetaConfigItem: ";
	std::cerr << std::endl;
	item->print(std::cerr, 20);

#endif

	/* extract info from KeyValueSet */
	std::list<RsTlvKeyValue>::iterator it;
	for(it = item->tlvkvs.pairs.begin(); it != item->tlvkvs.pairs.end(); it++)
	{
		/* find the configuration */
		uint32_t confId = atoi(it->key.c_str());
		std::string hashin = it->value;

		/*********************** HACK TO CHANGE CACHE CONFIG ID *********
		 * REMOVE IN A MONTH OR TWO
		 */

		if (confId == CONFIG_TYPE_CACHE_OLDID)
		{
			confId = CONFIG_TYPE_CACHE;
		}

		/*********************** HACK TO CHANGE CACHE CONFIG ID *********/

		std::map<uint32_t, pqiConfig *>::iterator cit;
		cit = configs.find(confId);
		if (cit != configs.end())
		{
#ifdef CONFIG_DEBUG
			std::cerr << "p3ConfigMgr::loadConfiguration() Element: ";
			std::cerr << confId << " Hash: " << hashin;
			std::cerr << std::endl;
#endif
			(cit->second)->loadConfiguration(hashin);

			/* force config to CHANGED to force saving into new non-global sig format */
			cit->second->IndicateConfigChanged();
	//		cit->second->HasConfigChanged(0);
	//		cit->second->HasConfigChanged(1);
		}
	}

	delete item;

#ifdef CONFIG_DEBUG
	std::cerr << "p3ConfigMgr::loadConfiguration() Done!";
	std::cerr << std::endl;
#endif

}


bool p3ConfigMgr::getSignAttempt(std::string& metaConfigFname, std::string& metaSignFname, BinMemInterface* membio){


	#ifdef CONFIG_DEBUG
	std::cerr << "p3ConfigMgr::getSignAttempt() metaConfigFname : " << metaConfigFname;
	std::cerr << std::endl;
	std::cerr << "p3ConfigMgr::getSignAttempt() metaSignFname : " << metaSignFname;
	std::cerr << std::endl;
	 #endif

	/* read signature */
	BinMemInterface *signbio = new BinMemInterface(1000, BIN_FLAGS_READABLE);

	if (!signbio->readfromfile(metaSignFname.c_str()))
	{
		#ifdef CONFIG_DEBUG
		std::cerr << "p3ConfigMgr::getSignAttempt() Failed to Load MetaSignFile";
		std::cerr << std::endl;
		#endif

		/* HACK to load the old one (with the wrong directory)
		 * THIS SHOULD BE REMOVED IN A COUPLE OF VERSIONS....
		 * ONLY HERE TO CORRECT BAD MISTAKE IN EARLIER VERSIONS.
		 */

		metaSignFname = metasigfname;
		metaConfigFname = metafname;

		if (!signbio->readfromfile(metaSignFname.c_str()))
		{
		#ifdef CONFIG_DEBUG
			std::cerr << "p3ConfigMgr::getSignAttempt() HACK: Failed to Load ALT MetaSignFile";
			std::cerr << std::endl;
		#endif
		}
		else
		{
		#ifdef CONFIG_DEBUG
			std::cerr << "p3ConfigMgr::getSignAttempt() HACK: Loaded ALT MetaSignFile";
			std::cerr << std::endl;
		#endif
		}
	}

	std::string oldsignature((char *) signbio->memptr(), signbio->memsize());
	delete signbio;



	if (!membio->readfromfile(metaConfigFname.c_str()))
	{
	#ifdef CONFIG_DEBUG
	std::cerr << "p3ConfigMgr::getSignAttempt() Failed to Load MetaFile";
	std::cerr << std::endl;
	#endif
	//		delete membio;
	//		return ;
	}

	/* get signature */
	std::string signature;
	AuthSSL::getAuthSSL()->SignData(membio->memptr(), membio->memsize(), signature);

	#ifdef CONFIG_DEBUG
	std::cerr << "p3ConfigMgr::getSignAttempt() New MetaFile Signature:";
	std::cerr << std::endl;
	std::cerr << signature;
	std::cerr << std::endl;
	#endif

	#ifdef CONFIG_DEBUG
	std::cerr << "p3ConfigMgr::getSignAttempt() Orig MetaFile Signature:";
	std::cerr << std::endl;
	std::cerr << oldsignature;
	std::cerr << std::endl;
	#endif

	if (signature != oldsignature)
	{
	/* Failed */
	#ifdef CONFIG_DEBUG
	std::cerr << "p3ConfigMgr::getSignAttempt() Signature Check Failed";
	std::cerr << std::endl;
	#endif
	return false;
	}

#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::getSignAttempt() Signature Check Passed!";
std::cerr << std::endl;
#endif

	return true;

}

void	p3ConfigMgr::addConfiguration(std::string file, pqiConfig *conf)
{
	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/

	/* construct filename */
	std::string filename = basedir;
	if (basedir != "")
	{
		filename += "/";
	}
	filename += file;

	conf->setFilename(filename);

	std::map<uint32_t, pqiConfig *>::iterator cit = configs.find(conf->Type());
	if (cit != configs.end())
	{
		std::cerr << "p3Config::addConfiguration() WARNING: type " << conf->Type();
		std::cerr << " with filename " << filename;
		std::cerr << " already added with filename " << cit->second->Filename() << std::endl;
	}
	configs[conf->Type()] = conf;
}


void    p3ConfigMgr::completeConfiguration()
{
	saveConfiguration();

	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
	mConfigSaveActive = false;
}

bool p3ConfigMgr::checkForGlobalSigConfig()
{
	bool oldTypeExists;
	FILE *metaFile = NULL, *metaSig = NULL;
	std::string fName = basedir + "/" + metafname;
	std::string sigName = basedir + "/" + metasigfname;

	metaFile = RsDirUtil::rs_fopen(fName.c_str(), "r");
	metaSig = RsDirUtil::rs_fopen(sigName.c_str(), "r");

	// check if files exist
	if((metaFile != NULL) && (metaSig != NULL))
	{
		oldTypeExists = true;
		fclose(metaFile);
		fclose(metaSig);
	}
	else
		oldTypeExists =  false;



	return oldTypeExists;


}

p3Config::p3Config(uint32_t t)
	:pqiConfig(t)
{
	return;
}


bool p3Config::loadConfiguration(std::string &loadHash)
{
	if(globalConfigType)
		return loadGlobalConfig(loadHash);
	else
		return loadConfig();
}

bool p3Config::loadConfig()
{

#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::loadConfig() loading Configuration\n File: " << Filename() << std::endl;
#endif

	bool pass = true;
	std::string cfgFname = Filename();
	std::string cfgFnameBackup = cfgFname + ".tmp";

	std::string signFname = Filename() +".sgn";
	std::string signFnameBackup = signFname + ".tmp";

	std::list<RsItem *> load;
	std::list<RsItem *>::iterator it;

	// try 1st attempt
	if(!loadAttempt(cfgFname, signFname, load))
	{

#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::loadConfig() Failed to Load" << std::endl;
#endif

		/* bad load */
		for(it = load.begin(); it != load.end(); it++)
		{
			delete (*it);
		}
		pass = false;

		load.clear();
	}

	// try 2nd attempt with backup files if first failed
	if(!pass)
	{
		if(!loadAttempt(cfgFnameBackup, signFnameBackup, load))
		{

#ifdef CONFIG_DEBUG
			std::cerr << "p3Config::loadConfig() Failed on 2nd Pass" << std::endl;
#endif

			/* bad load */
			for(it = load.begin(); it != load.end(); it++)
			{
				delete (*it);
			}
			pass = false;
		}
		else
			pass = true;
	}



	if(pass)
		loadList(load);
	else
		return false;

	return pass;
}

bool p3Config::loadAttempt(const std::string& cfgFname,const std::string& signFname, std::list<RsItem *>& load)
{

#ifdef CONFIG_DEBUG
	std::cerr << "p3Config::loadAttempt() \nFilename: " << cfgFname <<  std::endl;
#endif


	uint32_t bioflags = BIN_FLAGS_HASH_DATA | BIN_FLAGS_READABLE;
	uint32_t stream_flags = BIN_FLAGS_READABLE;

	BinEncryptedFileInterface *bio = new BinEncryptedFileInterface(cfgFname.c_str(), bioflags);
	pqiSSLstore stream(setupSerialiser(), "CONFIG", bio, stream_flags);

	if(!stream.getEncryptedItems(load))
	{
#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::loadAttempt() Error occurred trying to load Item" << std::endl;
#endif
		return false;
	}

	/* set hash */
	setHash(bio->gethash());

	BinMemInterface *signbio = new BinMemInterface(1000, BIN_FLAGS_READABLE);

	if(!signbio->readfromfile(signFname.c_str()))
	{
		delete signbio;
		return false;
	}

	std::string signatureStored((char *) signbio->memptr(), signbio->memsize());

	std::string signatureRead;
	std::string strHash(Hash());
	AuthSSL::getAuthSSL()->SignData(strHash.c_str(), strHash.length(), signatureRead);

	delete signbio;

	if(signatureRead != signatureStored)
		return false;

	return true;
}

bool	p3Config::loadGlobalConfig(std::string &loadHash)
{
	bool pass = false;
	std::string cfg_fname = Filename();
	std::string cfg_fname_backup = cfg_fname + ".tmp";
	std::string hashstr;
	std::list<RsItem *> load;

#ifdef CONFIG_DEBUG
	std::string success_fname = cfg_fname;
	std::cerr << "p3Config::loadConfiguration(): Attempting to load configuration file" << cfg_fname << std::endl;
#endif

	pass = getHashAttempt(loadHash, hashstr, cfg_fname, load);

	if(!pass){
                load.clear();
		pass = getHashAttempt(loadHash, hashstr, cfg_fname_backup, load);
#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::loadConfiguration() ERROR: Failed to get Hash from " << success_fname << std::endl;
		success_fname = cfg_fname_backup;
#endif

		if(!pass){
#ifdef CONFIG_DEBUG
			std::cerr << "p3Config::loadConfiguration() ERROR: Failed to get Hash from " << success_fname << std::endl;
#endif
			return false;
		}
	}

#ifdef CONFIG_DEBUG
	std::cerr << "p3Config::loadConfiguration(): SUCCESS: configuration file loaded" << success_fname << std::endl;
#endif

	setHash(hashstr);
	return loadList(load);
}




bool p3Config::getHashAttempt(const std::string& loadHash, std::string& hashstr,const std::string& cfg_fname,
		std::list<RsItem *>& load){

	std::list<RsItem *>::iterator it;

	uint32_t bioflags = BIN_FLAGS_HASH_DATA | BIN_FLAGS_READABLE;

	BinInterface *bio = new BinFileInterface(cfg_fname.c_str(), bioflags);
	PQInterface *stream = NULL;

	std::string tempString, msgConfigFileName;
	std::string filename = Filename() ;
	std::string::reverse_iterator rit = filename.rbegin();


	// get the msgconfig file name
	for(int i =0; (i <= 7) && (rit != filename.rend()); i++)
	{
		tempString.push_back(*rit);
		rit++;
	}

	rit = tempString.rbegin();

	for(; rit !=tempString.rend(); rit++)
		msgConfigFileName.push_back(*rit);

	if(msgConfigFileName == "msgs.cfg")
		stream = new pqiarchive(setupSerialiser(), bio, bioflags);
	else
		stream = new  pqistore(setupSerialiser(), "CONFIG", bio, bioflags);

	RsItem *item = NULL;

	while(NULL != (item = stream->GetItem()))
	{
#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::loadConfiguration() loaded item:";
		std::cerr << std::endl;
		item->print(std::cerr, 0);
		std::cerr << std::endl;
#endif
		load.push_back(item);
	}

#ifdef CONFIG_DEBUG
	std::cerr << "p3Config::loadConfiguration() loaded " << load.size();
	std::cerr << " Elements from File: " << cfg_fname;
	std::cerr << std::endl;
#endif

	/* check hash */
	hashstr = bio->gethash();

	// if hash then atmpt load with temporary file
	if (hashstr != loadHash)
	{

#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::loadConfiguration() ERROR: Hash != MATCHloaded";
		std::cerr << std::endl;
#endif

		/* bad load */
		for(it = load.begin(); it != load.end(); it++)
		{
			delete (*it);
		}

		setHash("");

		delete stream;
		return false;
	}

	delete stream;
	return true;
}


bool p3Config::saveConfiguration()
{
		return saveConfig();
}

bool p3Config::saveConfig()
{

	bool cleanup = true;
	std::list<RsItem *> toSave;
	saveList(cleanup, toSave);

	// temporarily append new to files as these will replace current configuration
	std::string newCfgFname = Filename() + "_new";
	std::string newSignFname = Filename() + ".sgn" + "_new";

	std::string tmpCfgFname = Filename() + ".tmp";
	std::string tmpSignFname = Filename() + ".sgn" + ".tmp";

	std::string cfgFname = Filename();
	std::string signFname = Filename() + ".sgn";


	uint32_t bioflags = BIN_FLAGS_HASH_DATA | BIN_FLAGS_WRITEABLE;
	uint32_t stream_flags = BIN_FLAGS_WRITEABLE;
	bool written = true;

	if (!cleanup)
		stream_flags |= BIN_FLAGS_NO_DELETE;

	BinEncryptedFileInterface *cfg_bio = new BinEncryptedFileInterface(newCfgFname.c_str(), bioflags);
	pqiSSLstore *stream = new pqiSSLstore(setupSerialiser(), "CONFIG", cfg_bio, stream_flags);

	written = written && stream->encryptedSendItems(toSave);

	if(!written)
		std::cerr << "(EE) Error while writing config file " << Filename() << ": file dropped!!" << std::endl;

	/* store the hash */
	setHash(cfg_bio->gethash());

	// bio is taken care of in stream's destructor, also forces file to close
	delete stream;

	/* sign data */
	std::string signature;
	std::string strHash(Hash());
	AuthSSL::getAuthSSL()->SignData(strHash.c_str(),strHash.length(), signature);

    /* write signature to configuration */
    BinMemInterface *signbio = new BinMemInterface(signature.c_str(),
    		signature.length(), BIN_FLAGS_READABLE);

    signbio->writetofile(newSignFname.c_str());

    delete signbio;

    // now rewrite current files to temp files
	// rename back-up to current file
	if(!RsDirUtil::renameFile(cfgFname, tmpCfgFname)  || !RsDirUtil::renameFile(signFname, tmpSignFname)){
#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::backedUpFileSave() Failed to rename backup meta files: " << std::endl
				<< cfgFname << " to " << tmpCfgFname << std::endl
				<< signFname << " to " << tmpSignFname << std::endl;
#endif
			written = false;
		}



	// now rewrite current files to temp files
	// rename back-up to current file
	if(!RsDirUtil::renameFile(newCfgFname, cfgFname)  || !RsDirUtil::renameFile(newSignFname, signFname)){
	#ifdef CONFIG_DEBUG
				std::cerr << "p3Config::() Failed to rename meta files: " << std::endl
						<< newCfgFname << " to " << cfgFname << std::endl
						<< newSignFname << " to " << signFname << std::endl;
	#endif

				written = false;
			}



	saveDone(); // callback to inherited class to unlock any Mutexes protecting saveList() data

	return written;

}


bool	p3Config::saveGlobalConfig()
{

	bool cleanup = true;
	std::list<RsItem *> toSave;
	saveList(cleanup, toSave);


	std::string cfg_fname = Filename(); // get configuration file name
	std::string cfg_fname_backup = Filename()+".tmp"; // backup file for two pass save

#ifdef CONFIG_DEBUG
        std::cerr << "Writting p3config file " << cfg_fname << std::endl ;
        std::cerr << "p3Config::saveConfiguration() toSave " << toSave.size();
	std::cerr << " Elements to File: " << cfg_fname;
	std::cerr << std::endl;
#endif

	// saves current config and keeps back-up (old configuration)
	if(!backedUpFileSave(cfg_fname, cfg_fname_backup, toSave, cleanup)){
#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::saveConfiguration(): Failed to save file" << std::endl;
#endif
		return false;
	}

	saveDone(); // callback to inherited class to unlock any Mutexes protecting saveList() data

	return true;
}


bool p3Config::backedUpFileSave(const std::string& cfg_fname, const std::string& cfg_fname_backup, std::list<RsItem* >& itemList,
		bool cleanup){

	uint32_t bioflags = BIN_FLAGS_HASH_DATA | BIN_FLAGS_WRITEABLE;
	uint32_t stream_flags = BIN_FLAGS_WRITEABLE;
	bool written = true;
	FILE* cfg_file = NULL;
	char* buff=NULL;
	int size_file = 0;

	if (!cleanup)
		stream_flags |= BIN_FLAGS_NO_DELETE;

	BinInterface *bio = new BinFileInterface(cfg_fname_backup.c_str(), bioflags);
	pqistore *stream = new pqistore(setupSerialiser(), "CONFIG", bio, stream_flags);

	std::list<RsItem *>::iterator it;

	for(it = itemList.begin(); it != itemList.end(); it++)
	{
#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::backedUpFileSave() save item:";
		std::cerr << std::endl;
		(*it)->print(std::cerr, 0);
		std::cerr << std::endl;
#endif
		written = written && stream->SendItem(*it);

#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::backedUpFileSave() saved ";
#endif

	}

	/* store the hash */
	setHash(bio->gethash());

	// bio is taken care of in stream's destructor
	delete stream;

#ifdef CONFIG_DEBUG
	std::cerr << "p3Config::backedUpFileSave() Save file and keeps a back up " << std::endl;
#endif

	// open file from which to collect buffer
	cfg_file = RsDirUtil::rs_fopen(cfg_fname.c_str(), "rb");

	// if it fails to open, create file,but back-up file will now be empty
	if(cfg_file == NULL){
	#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::backedUpFileSave()  fopen failed for file: " << cfg_fname << std::endl;
	#endif

		cfg_file = RsDirUtil::rs_fopen(cfg_fname.c_str(), "wb");

		if(cfg_file == NULL)
		{
			std::cerr << "p3Config::backedUpFileSave()  fopen failed for file:" << cfg_fname << std::endl;
			return false ;
		}
	}

	//determine file size
	fseek(cfg_file, 0L, SEEK_END);
	size_file = ftell(cfg_file);

	if(size_file < 0) // ftell returns -1 when fails
	{
		fclose(cfg_file);
		size_file = 0 ;
	}

	fseek(cfg_file, 0L, SEEK_SET);

#ifdef CONFIG_DEBUG
	std::cerr << "p3Config::backedUpFileSave(): Size of file: " << size_file << std::endl;
#endif

	// no point continuing, empty files all have same hash
	if(size_file > 0){

		//read this into a data buffer
		buff = new char[size_file];
		fread(buff, 1, size_file, cfg_file);
	}

	fclose(cfg_file);

	// rename back-up to current file, if this fails should return false hash's will not match at startup
	if(!RsDirUtil::renameFile(cfg_fname_backup, cfg_fname)){
#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::backedUpFileSave() Failed to rename file" << cfg_fname_backup << " to "
			<< cfg_fname << std::endl;
#endif
		written &= false; // at least one file save should be successful
	}

	if(size_file > 0)
	{

		// now write actual back-up file
		cfg_file = RsDirUtil::rs_fopen(cfg_fname_backup.c_str(), "wb");

		if(cfg_file == NULL){
#ifdef CONFIG_DEBUG
			std::cerr << "p3Config::backedUpFileSave()  fopen failed for file: " << cfg_fname_backup << std::endl;
#endif
		}

		if(size_file != (int) fwrite(buff, 1, size_file, cfg_file))
		{
			RsServer::notify()->AddSysMessage(0, RS_SYS_WARNING, "Write error", "Error while writing backup configuration file " + cfg_fname_backup + "\nIs your disc full or out of quota ?");
			fclose(cfg_file);
			delete[] buff;
			return false ;
		}


		fclose(cfg_file);

#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::backedUpFileSave() finished backed up save.  " << std::endl;
#endif


		delete[] buff;
		written |= true; // either backup or current file should have been saved
	}


#ifdef CONFIG_DEBUG
		std::cerr << "p3Config::backedUpFileSave() finished backed up save.  " << std::endl;
#endif

	return written;
}


/**************************** CONFIGURATION CLASSES ********************/

p3GeneralConfig::p3GeneralConfig()
	:p3Config(CONFIG_TYPE_GENERAL)
{
	return;
}

		// General Configuration System
std::string     p3GeneralConfig::getSetting(const std::string &opt)
{
#ifdef CONFIG_DEBUG
	std::cerr << "p3GeneralConfig::getSetting(" << opt << ")";
	std::cerr << std::endl;
#endif
	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/

	/* extract from config */
	std::map<std::string, std::string>::iterator it;
	if (settings.end() == (it = settings.find(opt)))
	{
		std::string nullstring;
		return nullstring;
	}
	return it->second;
}

void            p3GeneralConfig::setSetting(const std::string &opt, const std::string &val)
{
#ifdef CONFIG_DEBUG
	std::cerr << "p3GeneralConfig::setSetting(" << opt << " = " << val << ")";
	std::cerr << std::endl;
#endif
	{
		RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/

		/* extract from config */
		std::map<std::string, std::string>::iterator it;
		if (settings.end() != (it = settings.find(opt)))
		{
			if (it->second == val)
			{
				/* no change */
				return;
			}
		}

		settings[opt] = val;
	}
	/* outside mutex */
	IndicateConfigChanged();

	return;
}

RsSerialiser *p3GeneralConfig::setupSerialiser()
{
	RsSerialiser *rss = new RsSerialiser();
	rss->addSerialType(new RsGeneralConfigSerialiser());
	return rss;
}

bool p3GeneralConfig::saveList(bool &cleanup, std::list<RsItem *>& savelist)
{
	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/

#ifdef CONFIG_DEBUG
	std::cerr << "p3GeneralConfig::saveList() KV sets: " << settings.size();
	std::cerr << std::endl;
#endif

	cleanup = true;


	RsConfigKeyValueSet *item = new RsConfigKeyValueSet();
	std::map<std::string, std::string>::iterator it;
	for(it = settings.begin(); it != settings.end(); it++)
	{
		RsTlvKeyValue kv;
		kv.key = it->first;
		kv.value = it->second;
		item->tlvkvs.pairs.push_back(kv);

		/* make sure we don't overload it */
		if (item->tlvkvs.TlvSize() > 4000)
		{
			savelist.push_back(item);
			item = new RsConfigKeyValueSet();
		}
	}

	if (item->tlvkvs.pairs.size() > 0)
	{
		savelist.push_back(item);
	}

	return true;
}


bool    p3GeneralConfig::loadList(std::list<RsItem *>& load)
{
#ifdef CONFIG_DEBUG
	std::cerr << "p3GeneralConfig::loadList() count: " << load.size();
	std::cerr << std::endl;
#endif

	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/

	/* add into settings */
	RsConfigKeyValueSet *item = NULL;
	std::list<RsItem *>::iterator it;
	std::list<RsTlvKeyValue>::iterator kit;

	for(it = load.begin(); it != load.end();)
	{
		item = dynamic_cast<RsConfigKeyValueSet *>(*it);
		if (item)
		{
			for(kit = item->tlvkvs.pairs.begin();
				kit != item->tlvkvs.pairs.end(); kit++)
			{
				settings[kit->key] = kit->value;
			}
		}

		/* cleanup */
		delete (*it);
		it = load.erase(it);
	}

	return true;
}


/**** MUTEX NOTE:
 * have protected all, but think that
 * only the Indication and hash really need it
 */

bool pqiConfig::globalConfigType = false;

pqiConfig::pqiConfig(uint32_t t)
	: cfgMtx("pqiConfig"), ConfInd(2), type(t)
{
	return;
}

pqiConfig::~pqiConfig()
{
	return;
}

uint32_t   pqiConfig::Type()
{
	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
	return type;
}

const std::string& pqiConfig::Filename()
{
	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
	return filename;
}

const std::string& pqiConfig::Hash()
{
	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
	return hash;
}

void	pqiConfig::IndicateConfigChanged()
{
	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
	ConfInd.IndicateChanged();
}

bool	pqiConfig::HasConfigChanged(uint16_t idx)
{
	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
	return  ConfInd.Changed(idx);
}

void    pqiConfig::setFilename(const std::string& name)
{
	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
	filename = name;
}

void	pqiConfig::setHash(const std::string& h)
{
	RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
	hash = h;
}

