/* * 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 #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::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::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 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::saveConfiguration() 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; int size_file, size_sign; // 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 } #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 = fopen(fname.c_str(), "rb"); sign = fopen(sign_fname.c_str(), "rb"); // if failed then just use back-up file: back-up and actual configs are the same files now if((file == NULL) || (sign == NULL)){ #ifdef CONFIG_DEBUG std::cerr << "p3Config::backedUpFileSave() failed to open meta files " << fname << std::endl; #endif fclose(file); fclose(file); file = fopen(fname_backup.c_str(), "rb"); sign = fopen(sign_fname_backup.c_str(), "rb"); 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); //read this into a buffer char* config_buff = new char[size_file]; fread(config_buff, size_file, 1, file); fclose(file); //read this into a buffer char* sign_buff = new char[size_sign]; fread(sign_buff, size_sign, 1, sign); fclose(sign); // rename back-up to current file if(!RsDirUtil::renameFile(fname_backup, fname) || !RsDirUtil::renameFile(sign_fname_backup, sign_fname)){ 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; return false; } // now write actual back-up file file = fopen(fname_backup.c_str(), "wb"); sign = 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 return false; } fwrite(config_buff, size_file, 1, file); fwrite(sign_buff, size_sign, 1, sign); 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() { 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(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::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::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 } 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); 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::string cfg_fname = Filename(); std::string cfg_fname_backup = cfg_fname + ".tmp"; std::string hashstr; std::list load; #ifdef CONFIG_DEBUG std::string success_fname = cfg_fname; std::cerr << "p3Config::loadConfiguration(): Attempting to load configuration file" << cfg_fname << std::endl; #endif bool 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& load){ std::list::iterator it; uint32_t bioflags = BIN_FLAGS_HASH_DATA | BIN_FLAGS_READABLE; uint32_t stream_flags = BIN_FLAGS_READABLE; BinInterface *bio = new BinFileInterface(cfg_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: " << 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(""); return false; } //delete bio; return true; } bool p3Config::saveConfiguration() { bool cleanup = true; std::list toSave = saveList(cleanup); 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 saveDone(); // callback to inherited class to unlock any Mutexes protecting saveList() data // saves current config and keeps back-up (old configuration) if(!backedUpFileSave(cfg_fname, cfg_fname_backup, toSave, cleanup)) return false; return true; } bool p3Config::backedUpFileSave(const std::string& cfg_fname, const std::string& cfg_fname_backup, std::list& itemList, bool cleanup){ uint32_t bioflags = BIN_FLAGS_HASH_DATA | BIN_FLAGS_WRITEABLE; uint32_t stream_flags = true; if (!cleanup){ stream_flags |= BIN_FLAGS_NO_DELETE; }else{ stream_flags = BIN_FLAGS_WRITEABLE; } BinInterface *bio = new BinFileInterface(cfg_fname_backup.c_str(), bioflags); pqistore *stream = new pqistore(setupSerialiser(), "CONFIG", bio, stream_flags); std::list::iterator it; bool written = true; for(it = itemList.begin(); it != itemList.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); } /* 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 FILE* cfg_file = NULL; int size_file = 0; cfg_file = fopen(cfg_fname.c_str(), "rb"); // if failed then just use back-up file: back-up and file are now the same files if(cfg_file == NULL){ #ifdef CONFIG_DEBUG std::cerr << "p3Config::backedUpFileSave() fopen failed for file: " << cfg_fname << std::endl; #endif fclose(cfg_file); cfg_file = fopen(cfg_fname_backup.c_str(), "rb"); if(cfg_file == NULL){ std::cerr << "p3Config::backedUpFileSave() fopen failed for file:" << cfg_fname_backup << std::endl; return false; } } //determine file size fseek(cfg_file, 0L, SEEK_END); size_file = ftell(cfg_file); fseek(cfg_file, 0L, SEEK_SET); //read this into a data buffer char* buff = new char[size_file]; fread(buff, size_file, 1, cfg_file); fclose(cfg_file); // rename back-up to current file if(!RsDirUtil::renameFile(cfg_fname_backup, cfg_fname)){ std::cerr << "p3Config::backedUpFileSave() Failed to rename file" << cfg_fname_backup << " to " << cfg_fname << std::endl; return false; } // now write actual back-up file cfg_file = 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 return false; } fwrite(buff, size_file, 1, cfg_file); fclose(cfg_file); #ifdef CONFIG_DEBUG std::cerr << "p3Config::backedUpFileSave() finished backed up save. " << std::endl; #endif delete[] buff; 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::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::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 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 savelist; RsConfigKeyValueSet *item = new RsConfigKeyValueSet(); std::map::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 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::iterator it; std::list::iterator kit; for(it = load.begin(); it != load.end();) { item = dynamic_cast(*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; }