RetroShare/libretroshare/src/pqi/p3cfgmgr.cc

720 lines
16 KiB
C++
Raw Normal View History

/*
* 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 "rsiface/rspeers.h"
#include "pqi/p3cfgmgr.h"
#include "pqi/authssl.h"
#include "pqi/pqibin.h"
#include "pqi/pqistore.h"
#include "pqi/pqinotify.h"
#include <errno.h>
#include "serialiser/rsconfigitems.h"
/****
* #define CONFIG_DEBUG 1
***/
p3ConfigMgr::p3ConfigMgr(std::string dir, std::string fname, std::string signame)
:basedir(dir), metafname(fname), metasigfname(signame),
mConfigSaveActive(true)
{
}
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()
{
RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::saveConfiguration()";
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::saveConfiguration() Saving Element: ";
std::cerr << it->first;
std::cerr << std::endl;
#endif
it->second->saveConfiguration();
}
/* save metaconfig */
#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::saveConfiguration() 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;
{
std::ostringstream out;
out << it->first;
kv.key = out.str();
}
kv.value = it->second->Hash();
item->tlvkvs.pairs.push_back(kv);
}
#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::saveConfiguration() Complete MetaConfigItem: ";
std::cerr << std::endl;
item->print(std::cerr, 20);
#endif
/* construct filename */
std::string filename1 = basedir;
std::string filename2 = basedir;
if (basedir != "")
{
filename1 += "/";
filename2 += "/";
}
filename1 += metasigfname;
filename2 += metafname;
/* Write the data to a stream */
uint32_t bioflags = BIN_FLAGS_WRITEABLE;
BinMemInterface *membio = new BinMemInterface(1000, bioflags);
RsSerialiser *rss = new RsSerialiser();
rss->addSerialType(new RsGeneralConfigSerialiser());
pqistore store(rss, "CONFIG", membio, BIN_FLAGS_WRITEABLE);
store.SendItem(item);
/* sign data */
std::string signature;
AuthSSL::getAuthSSL()->SignData(membio->memptr(), membio->memsize(), signature);
#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::saveConfiguration() MetaFile Signature:";
std::cerr << std::endl;
std::cerr << signature;
std::cerr << std::endl;
#endif
if (!membio->writetofile(filename2.c_str()))
{
#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::saveConfiguration() Failed to Write MetaFile " << filename2 ;
std::cerr << std::endl;
#endif
}
/* write signature to configuration */
BinMemInterface *signbio = new BinMemInterface(signature.c_str(),
signature.length(), BIN_FLAGS_READABLE);
if (!signbio->writetofile(filename1.c_str()))
{
#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::saveConfiguration() Failed to Write MetaSignFile" << filename1 ;
std::cerr << std::endl;
#endif
}
delete signbio;
}
void p3ConfigMgr::loadConfiguration()
{
RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::loadConfiguration()";
std::cerr << std::endl;
#endif
/* construct filename */
std::string filename1 = basedir;
std::string filename2 = basedir;
if (basedir != "")
{
filename1 += "/";
filename2 += "/";
}
filename1 += metasigfname;
filename2 += metafname;
#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::loadConfiguration() filename1 : " << filename1;
std::cerr << std::endl;
std::cerr << "p3ConfigMgr::loadConfiguration() filename2 : " << filename2;
std::cerr << std::endl;
#endif
/* write signature to configuration */
BinMemInterface *signbio = new BinMemInterface(1000, BIN_FLAGS_READABLE);
if (!signbio->readfromfile(filename1.c_str()))
{
#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::loadConfiguration() 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.
*/
filename1 = metasigfname;
filename2 = metafname;
if (!signbio->readfromfile(filename1.c_str()))
{
#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::loadConfiguration() HACK: Failed to Load ALT MetaSignFile";
std::cerr << std::endl;
#endif
}
else
{
#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::loadConfiguration() HACK: Loaded ALT MetaSignFile";
std::cerr << std::endl;
#endif
}
}
std::string oldsignature((char *) signbio->memptr(), signbio->memsize());
delete signbio;
BinMemInterface *membio = new BinMemInterface(1000, BIN_FLAGS_READABLE);
if (!membio->readfromfile(filename2.c_str()))
{
#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::loadConfiguration() 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::loadConfiguration() New MetaFile Signature:";
std::cerr << std::endl;
std::cerr << signature;
std::cerr << std::endl;
#endif
#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::loadConfiguration() 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::loadConfiguration() Signature Check Failed";
std::cerr << std::endl;
#endif
return;
}
membio->fseek(0); /* go to start */
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 NOT CHANGED */
cit->second->HasConfigChanged(0);
cit->second->HasConfigChanged(1);
}
}
delete item;
#ifdef CONFIG_DEBUG
std::cerr << "p3ConfigMgr::loadConfiguration() Done!";
std::cerr << std::endl;
#endif
}
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);
configs[conf->Type()] = conf;
}
void p3ConfigMgr::completeConfiguration()
{
saveConfiguration();
RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
mConfigSaveActive = false;
}
p3Config::p3Config(uint32_t t)
:pqiConfig(t)
{
return;
}
bool p3Config::loadConfiguration(std::string &loadHash)
{
std::list<RsItem *> load;
std::list<RsItem *>::iterator it;
std::string fname = Filename();
uint32_t bioflags = BIN_FLAGS_HASH_DATA | BIN_FLAGS_READABLE;
uint32_t stream_flags = BIN_FLAGS_READABLE;
BinInterface *bio = new BinFileInterface(fname.c_str(), bioflags);
pqistore stream(setupSerialiser(), "CONFIG", bio, stream_flags);
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: " << fname;
std::cerr << std::endl;
#endif
/* check hash */
std::string hashstr = bio->gethash();
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("");
return false;
}
setHash(hashstr);
/* else okay */
return loadList(load);
}
bool p3Config::saveConfiguration()
{
bool cleanup = true;
std::list<RsItem *> toSave = saveList(cleanup);
std::string fname = Filename();
std::string fnametmp = Filename()+".tmp";
#ifdef CONFIG_DEBUG
std::cerr << "Writting p3config file " << fname.c_str() << std::endl ;
std::cerr << "p3Config::saveConfiguration() toSave " << toSave.size();
std::cerr << " Elements to File: " << fname;
std::cerr << std::endl;
#endif
uint32_t bioflags = BIN_FLAGS_HASH_DATA | BIN_FLAGS_WRITEABLE;
uint32_t stream_flags = BIN_FLAGS_WRITEABLE;
if (!cleanup)
stream_flags |= BIN_FLAGS_NO_DELETE;
BinInterface *bio = new BinFileInterface(fnametmp.c_str(), bioflags);
pqistore *stream = new pqistore(setupSerialiser(), "CONFIG", bio, stream_flags);
std::list<RsItem *>::iterator it;
bool written = true ;
for(it = toSave.begin(); it != toSave.end(); it++)
{
#ifdef CONFIG_DEBUG
std::cerr << "p3Config::saveConfiguration() save item:";
std::cerr << std::endl;
(*it)->print(std::cerr, 0);
std::cerr << std::endl;
#endif
written = written && stream->SendItem(*it);
// std::cerr << "written = " << written << std::endl ;
}
/* store the hash */
setHash(bio->gethash());
saveDone(); /* callback to inherited class to unlock any Mutexes
* protecting saveList() data
*/
delete stream ;
if(!written)
return false ;
std::cerr << "renaming " << fnametmp.c_str() << " to " << fname.c_str() << std::endl ;
if(!RsDirUtil::renameFile(fnametmp,fname))
{
std::ostringstream errlog;
#ifdef WIN32
errlog << "Error " << GetLastError() ;
#else
errlog << "Error " << errno ;
#endif
getPqiNotify()->AddSysMessage(0, RS_SYS_WARNING, "File rename error", "Error while renaming file " + fname + ": got error "+errlog.str());
return false ;
}
std::cerr << "Successfully wrote p3config file " << fname.c_str() << std::endl ;
/* else okay */
return true;
}
/**************************** CONFIGURATION CLASSES ********************/
p3GeneralConfig::p3GeneralConfig()
:p3Config(CONFIG_TYPE_GENERAL)
{
return;
}
// General Configuration System
std::string p3GeneralConfig::getSetting(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(std::string opt, 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;
}
std::list<RsItem *> p3GeneralConfig::saveList(bool &cleanup)
{
RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
#ifdef CONFIG_DEBUG
std::cerr << "p3GeneralConfig::saveList() KV sets: " << settings.size();
std::cerr << std::endl;
#endif
cleanup = true;
std::list<RsItem *> savelist;
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 savelist;
}
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
*/
pqiConfig::pqiConfig(uint32_t t)
:ConfInd(2), type(t)
{
return;
}
pqiConfig::~pqiConfig()
{
return;
}
uint32_t pqiConfig::Type()
{
RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
return type;
}
std::string pqiConfig::Filename()
{
RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
return filename;
}
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(std::string name)
{
RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
filename = name;
}
void pqiConfig::setHash(std::string h)
{
RsStackMutex stack(cfgMtx); /***** LOCK STACK MUTEX ****/
hash = h;
}