diff --git a/i2pd_qt.pro b/i2pd_qt.pro index a26fb59..58667c7 100644 --- a/i2pd_qt.pro +++ b/i2pd_qt.pro @@ -29,6 +29,7 @@ CONFIG(debug, debug|release) { DEFINES += I2PD_QT_DEBUG I2PDMAKE += DEBUG=yes + QMAKE_CXXFLAGS_DEBUG += "-O0" } CONFIG(release, debug|release) { message(Release build) @@ -38,6 +39,7 @@ CONFIG(release, debug|release) { SOURCES += \ src/DaemonQT.cpp \ + src/MutexWrapperLock.cpp \ src/mainwindow.cpp \ src/ClientTunnelPane.cpp \ src/MainWindowItems.cpp \ @@ -64,7 +66,9 @@ SOURCES += \ src/I2pdQtUtil.cpp HEADERS += \ + src/ConcurrentHolder.h \ src/DaemonQT.h \ + src/MutexWrapperLock.h \ src/mainwindow.h \ src/ClientTunnelPane.h \ src/MainWindowItems.h \ diff --git a/src/ConcurrentHolder.h b/src/ConcurrentHolder.h new file mode 100644 index 0000000..8d92f46 --- /dev/null +++ b/src/ConcurrentHolder.h @@ -0,0 +1,33 @@ +#ifndef ConcurrentHolder_H +#define ConcurrentHolder_H + +#include +#include "MutexWrapperLock.h" +#include + +template +class ConcurrentHolder +{ +public: + ConcurrentHolder(HeldDataType _data) { + data = _data; + mutex = new QRecursiveMutex(); + assert(mutex!=nullptr); + } + ~ConcurrentHolder() { + assert(mutex!=nullptr); + delete mutex; + } + QRecursiveMutex* getMutex() { + assert(mutex!=nullptr); + return mutex; + } + HeldDataType& getData() { + return data; + } +private: + HeldDataType data; + QRecursiveMutex* mutex; +}; + +#endif // ConcurrentHolder_H diff --git a/src/MutexWrapperLock.cpp b/src/MutexWrapperLock.cpp new file mode 100644 index 0000000..85e82c9 --- /dev/null +++ b/src/MutexWrapperLock.cpp @@ -0,0 +1,10 @@ +#include "MutexWrapperLock.h" + +MutexWrapperLock::MutexWrapperLock(QRecursiveMutex* _mutex) { + mutex = _mutex; + _mutex->lock(); +} + +MutexWrapperLock::~MutexWrapperLock() { + mutex->unlock(); +} diff --git a/src/MutexWrapperLock.h b/src/MutexWrapperLock.h new file mode 100644 index 0000000..7f67c64 --- /dev/null +++ b/src/MutexWrapperLock.h @@ -0,0 +1,15 @@ +#ifndef MUTEXWRAPPERLOCK_H +#define MUTEXWRAPPERLOCK_H + +#include + +class MutexWrapperLock +{ +public: + MutexWrapperLock(QRecursiveMutex* _mutex); + ~MutexWrapperLock(); +private: + QRecursiveMutex* mutex; +}; + +#endif // MUTEXWRAPPERLOCK_H diff --git a/src/SaverImpl.cpp b/src/SaverImpl.cpp index 5a12c29..b5a033a 100644 --- a/src/SaverImpl.cpp +++ b/src/SaverImpl.cpp @@ -9,8 +9,8 @@ #include "mainwindow.h" -SaverImpl::SaverImpl(MainWindow *mainWindowPtr_, QList* configItems_, std::map* tunnelConfigs_) : - configItems(configItems_), tunnelConfigs(tunnelConfigs_), confpath(), tunconfpath(), mainWindowPtr(mainWindowPtr_) +SaverImpl::SaverImpl(MainWindow *mainWindowPtr_) : + confpath(), tunconfpath(), mainWindowPtr(mainWindowPtr_) { QObject::connect(this, SIGNAL(showPreventedSaveTunnelsConfMessage()), mainWindowPtr, SLOT(showTunnelsPagePreventedMessage())); } @@ -18,10 +18,13 @@ SaverImpl::SaverImpl(MainWindow *mainWindowPtr_, QList* configI SaverImpl::~SaverImpl() {} bool SaverImpl::save(bool reloadAfterSave, const FocusEnum focusOn, const std::string& tunnelNameToFocus, QWidget* widgetToFocus) { + MutexWrapperLock lock(mainWindowPtr->volatileDataHolder->getMutex()); + MainWindow::VolatileData* vdata = mainWindowPtr->volatileDataHolder->getData(); + //save main config { std::stringstream out; - for(QList::iterator it = configItems->begin(); it!= configItems->end(); ++it) { + for(QList::iterator it = vdata->configItems.begin(); it!= vdata->configItems.end(); ++it) { MainWindowItem* item = *it; item->saveToStringStream(out); } @@ -44,9 +47,9 @@ bool SaverImpl::save(bool reloadAfterSave, const FocusEnum focusOn, const std::s }else{ std::stringstream out; - for (std::map::iterator it=tunnelConfigs->begin(); it!=tunnelConfigs->end(); ++it) { + for (const auto& it : vdata->tunnelConfigs) { //const std::string& name = it->first; - TunnelConfig* tunconf = it->second; + TunnelConfig* tunconf = it.second; tunconf->saveHeaderToStringStream(out); tunconf->saveToStringStream(out); tunconf->saveI2CPParametersToStringStream(out); diff --git a/src/SaverImpl.h b/src/SaverImpl.h index 6f8ab91..7d0e166 100644 --- a/src/SaverImpl.h +++ b/src/SaverImpl.h @@ -16,14 +16,12 @@ class TunnelConfig; class SaverImpl : public Saver { public: - SaverImpl(MainWindow *mainWindowPtr_, QList* configItems_, std::map* tunnelConfigs_); + SaverImpl(MainWindow *mainWindowPtr_); virtual ~SaverImpl(); virtual bool save(bool reloadAfterSave, const FocusEnum focusOn, const std::string& tunnelNameToFocus, QWidget* widgetToFocus); void setConfPath(QString& confpath_); void setTunnelsConfPath(QString& tunconfpath_); private: - QList* configItems; - std::map* tunnelConfigs; QString confpath; QString tunconfpath; MainWindow* mainWindowPtr; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4537a27..0d1755e 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -63,23 +63,24 @@ MainWindow::MainWindow(std::shared_ptr logStream_, QWidget *paren ,routerCommandsParent(new QWidget(this)) ,widgetlocks() ,i2pController(nullptr) - ,configItems() ,datadir() ,confpath() ,tunconfpath() - ,tunnelConfigs() - ,tunnelConfigsById() + ,ignoreUpdatesOnAppendForms(false) + ,volatileDataHolder(new ConcurrentHolder(new VolatileData())) ,tunnelsPageUpdateListener(this) ,preventSaveTunnelsBool(false) - ,saverPtr( - new SaverImpl(this, - &configItems, - &tunnelConfigs)) - + ,saverPtr(nullptr) { assert(delayedSaveManagerPtr!=nullptr); + assert(volatileDataHolder!=nullptr); + assert(volatileDataHolder->getData()!=nullptr); + saverPtr=new SaverImpl(this); assert(saverPtr!=nullptr); + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + ui->setupUi(this); statusButtonsUI->setupUi(ui->statusButtonsPane); routerCommandsUI->setupUi(routerCommandsParent); @@ -209,8 +210,8 @@ MainWindow::MainWindow(std::shared_ptr logStream_, QWidget *paren initFolderChooser( OPTION("","datadir",[]{return "";}), uiSettings->dataFolderLineEdit, uiSettings->dataFolderBrowsePushButton); initIPAddressBox( OPTION("","host",[]{return "";}), uiSettings->routerExternalHostLineEdit, tr("Router external address -> Host")); initTCPPortBox( OPTION("","port",[]{return "";}), uiSettings->routerExternalPortLineEdit, tr("Router external address -> Port")); - daemonOption=initNonGUIOption( OPTION("","daemon",[]{return "";})); - serviceOption=initNonGUIOption( OPTION("","service",[]{return "";})); + daemonOption=initNonGUIOption( OPTION("","daemon",[]{return "";}),type_bool); + serviceOption=initNonGUIOption( OPTION("","service",[]{return "";}),type_bool); initStringBox( OPTION("","ifname4",[]{return "";}), uiSettings->ifname4LineEdit);//Network interface to bind to for IPv4 initStringBox( OPTION("","ifname6",[]{return "";}), uiSettings->ifname6LineEdit);//Network interface to bind to for IPv6 initCheckBox( OPTION("","nat",[]{return "true";}), uiSettings->natCheckBox);//If true, assume we are behind NAT. true by default @@ -389,7 +390,7 @@ MainWindow::MainWindow(std::shared_ptr logStream_, QWidget *paren uiSettings->configFileLineEdit->setText(confpath); uiSettings->tunnelsConfigFileLineEdit->setText(tunconfpath); - for(QList::iterator it = configItems.begin(); it!= configItems.end(); ++it) { + for(QList::iterator it = vdata->configItems.begin(); it!= vdata->configItems.end(); ++it) { MainWindowItem* item = *it; item->installListeners(this); } @@ -665,11 +666,13 @@ MainWindow::~MainWindow() delete statusPageUpdateTimer; delete delayedSaveManagerPtr; delete saverPtr; - for(QList::iterator it = configItems.begin(); it!= configItems.end(); ++it) { + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + for(QList::iterator it = vdata->configItems.begin(); it!= vdata->configItems.end(); ++it) { MainWindowItem* item = *it; item->deleteLater(); } - configItems.clear(); + vdata->configItems.clear(); //QMessageBox::information(0, "Debug", "mw destructor 1"); //delete ui; //QMessageBox::information(0, "Debug", "mw destructor 2"); @@ -679,53 +682,82 @@ FileChooserItem* MainWindow::initFileChooser(ConfigOption option, QLineEdit* fil FileChooserItem* retVal; retVal=new FileChooserItem(option, fileNameLineEdit, fileBrowsePushButton, this, requireExistingFile, readOnly); MainWindowItem* super=retVal; - configItems.append(super); + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + vdata->configItems.append(super); return retVal; } void MainWindow::initFolderChooser(ConfigOption option, QLineEdit* folderLineEdit, QPushButton* folderBrowsePushButton){ - configItems.append(new FolderChooserItem(option, folderLineEdit, folderBrowsePushButton, this, true)); + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + vdata->configItems.append(new FolderChooserItem(option, folderLineEdit, folderBrowsePushButton, this, true)); } /*void MainWindow::initCombobox(ConfigOption option, QComboBox* comboBox){ configItems.append(new ComboBoxItem(option, comboBox)); QObject::connect(comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(saveAllConfigs())); }*/ void MainWindow::initLogDestinationCombobox(ConfigOption option, QComboBox* comboBox){ - configItems.append(new LogDestinationComboBoxItem(option, comboBox)); + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + vdata->configItems.append(new LogDestinationComboBoxItem(option, comboBox)); } void MainWindow::initLogLevelCombobox(ConfigOption option, QComboBox* comboBox){ - configItems.append(new LogLevelComboBoxItem(option, comboBox)); + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + vdata->configItems.append(new LogLevelComboBoxItem(option, comboBox)); } void MainWindow::initSignatureTypeCombobox(ConfigOption option, QComboBox* comboBox){ - configItems.append(new SignatureTypeComboBoxItem(option, comboBox)); + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + vdata->configItems.append(new SignatureTypeComboBoxItem(option, comboBox)); } void MainWindow::initIPAddressBox(ConfigOption option, QLineEdit* addressLineEdit, QString fieldNameTranslated){ - configItems.append(new IPAddressStringItem(option, addressLineEdit, fieldNameTranslated, this)); + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + vdata->configItems.append(new IPAddressStringItem(option, addressLineEdit, fieldNameTranslated, this)); } void MainWindow::initTCPPortBox(ConfigOption option, QLineEdit* portLineEdit, QString fieldNameTranslated){ - configItems.append(new TCPPortStringItem(option, portLineEdit, fieldNameTranslated, this)); + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + vdata->configItems.append(new TCPPortStringItem(option, portLineEdit, fieldNameTranslated, this)); } void MainWindow::initCheckBox(ConfigOption option, QCheckBox* checkBox) { - configItems.append(new CheckBoxItem(option, checkBox)); + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + vdata->configItems.append(new CheckBoxItem(option, checkBox)); } void MainWindow::initIntegerBox(ConfigOption option, QLineEdit* numberLineEdit, QString fieldNameTranslated){ - configItems.append(new IntegerStringItem(option, numberLineEdit, fieldNameTranslated, this)); + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + vdata->configItems.append(new IntegerStringItem(option, numberLineEdit, fieldNameTranslated, this)); } void MainWindow::initUInt32Box(ConfigOption option, QLineEdit* numberLineEdit, QString fieldNameTranslated){ - configItems.append(new UInt32StringItem(option, numberLineEdit, fieldNameTranslated, this)); + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + vdata->configItems.append(new UInt32StringItem(option, numberLineEdit, fieldNameTranslated, this)); } void MainWindow::initUInt16Box(ConfigOption option, QLineEdit* numberLineEdit, QString fieldNameTranslated){ - configItems.append(new UInt16StringItem(option, numberLineEdit, fieldNameTranslated, this)); + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + vdata->configItems.append(new UInt16StringItem(option, numberLineEdit, fieldNameTranslated, this)); } void MainWindow::initStringBox(ConfigOption option, QLineEdit* lineEdit){ - configItems.append(new BaseStringItem(option, lineEdit, QString(), this)); + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + vdata->configItems.append(new BaseStringItem(option, lineEdit, QString(), this)); } -NonGUIOptionItem* MainWindow::initNonGUIOption(ConfigOption option) { +NonGUIOptionItem* MainWindow::initNonGUIOption(ConfigOption option, TypeEnum optionValueType) { NonGUIOptionItem * retValue; - configItems.append(retValue=new NonGUIOptionItem(option)); + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + vdata->configItems.append(retValue=new NonGUIOptionItem(option)); + retValue->optionValueType=optionValueType; return retValue; } void MainWindow::loadAllConfigs(SaverImpl* saverPtr){ + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); //BORROWED FROM ??? //TODO move this code into single location std::string config; i2p::config::GetOption("conf", config); @@ -769,7 +801,7 @@ void MainWindow::loadAllConfigs(SaverImpl* saverPtr){ saverPtr->setConfPath(this->confpath); saverPtr->setTunnelsConfPath(this->tunconfpath); - for(QList::iterator it = configItems.begin(); it!= configItems.end(); ++it) { + for(QList::iterator it = vdata->configItems.begin(); it!= vdata->configItems.end(); ++it) { MainWindowItem* item = *it; item->loadFromConfigOption(); } @@ -788,12 +820,14 @@ void MainWindow::DisableTunnelsPage() { } void MainWindow::layoutTunnels() { + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); int height=0; ui->tunnelsScrollAreaWidgetContents->setGeometry(0,0,0,0); - for(std::map::iterator it = tunnelConfigs.begin(); it != tunnelConfigs.end(); ++it) { - //const std::string& name=it->first; - TunnelConfig* tunconf = it->second; + for(const auto& it : vdata->tunnelConfigs) { + //const std::string& name=it.first; + TunnelConfig* tunconf = it.second; TunnelPane * tunnelPane=tunconf->getTunnelPane(); if(!tunnelPane)continue; int h=tunnelPane->height(); @@ -809,9 +843,12 @@ void MainWindow::layoutTunnels() { } void MainWindow::deleteTunnelFromUI(std::string tunnelName, TunnelConfig* cnf) { + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + TunnelPane* tp = cnf->getTunnelPane(); if(!tp)return; - tunnelPanes.remove(tp); + vdata->tunnelPanes.remove(tp); tp->deleteWidget(); layoutTunnels(); } @@ -825,7 +862,10 @@ bool MainWindow::saveAllConfigs(bool reloadAfterSave, FocusEnum focusOn, std::st daemonOption->optionValue=boost::any(false); serviceOption->optionValue=boost::any(false); - for(QList::iterator it = configItems.begin(); it!= configItems.end(); ++it) { + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + + for(QList::iterator it = vdata->configItems.begin(); it != vdata->configItems.end(); ++it) { MainWindowItem* item = *it; bool alreadyDisplayedIfWrong=false; if(!item->isValid(alreadyDisplayedIfWrong)){ @@ -884,34 +924,39 @@ void MainWindow::updated() { void MainWindowItem::installListeners(MainWindow *mainWindow) {} void MainWindow::appendTunnelForms(std::string tunnelNameToFocus) { + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + + ignoreUpdatesOnAppendForms = true; + int height=0; ui->tunnelsScrollAreaWidgetContents->setGeometry(0,0,0,0); - for(std::map::iterator it = tunnelConfigs.begin(); it != tunnelConfigs.end(); ++it) { - const std::string& name=it->first; - TunnelConfig* tunconf = it->second; - ServerTunnelConfig* stc = tunconf->asServerTunnelConfig(); + std::unordered_map tunnelConfigsCopy(vdata->tunnelConfigs); + for(const auto& it : tunnelConfigsCopy) { + const std::string& name=it.first; + ServerTunnelConfig* stc = it.second->asServerTunnelConfig(); if(stc){ ServerTunnelPane * tunnelPane=new ServerTunnelPane(&tunnelsPageUpdateListener, stc, ui->wrongInputLabel, ui->wrongInputLabel, this); - tunconf->setTunnelPane(tunnelPane); - int h=tunnelPane->appendServerTunnelForm(stc, ui->tunnelsScrollAreaWidgetContents, tunnelPanes.size(), height); + stc->setTunnelPane(tunnelPane); + int h=tunnelPane->appendServerTunnelForm(stc, ui->tunnelsScrollAreaWidgetContents, vdata->tunnelPanes.size(), height); height+=h; //qDebug() << "tun.height:" << height << "sz:" << tunnelPanes.size(); - tunnelPanes.push_back(tunnelPane); - if(name==tunnelNameToFocus){ + vdata->tunnelPanes.push_back(tunnelPane); + if(name.compare(tunnelNameToFocus)==0){ tunnelPane->getNameLineEdit()->setFocus(); ui->tunnelsScrollArea->ensureWidgetVisible(tunnelPane->getNameLineEdit()); } continue; } - ClientTunnelConfig* ctc = tunconf->asClientTunnelConfig(); + ClientTunnelConfig* ctc = it.second->asClientTunnelConfig(); if(ctc){ ClientTunnelPane * tunnelPane=new ClientTunnelPane(&tunnelsPageUpdateListener, ctc, ui->wrongInputLabel, ui->wrongInputLabel, this); - tunconf->setTunnelPane(tunnelPane); - int h=tunnelPane->appendClientTunnelForm(ctc, ui->tunnelsScrollAreaWidgetContents, tunnelPanes.size(), height); + ctc->setTunnelPane(tunnelPane); + int h=tunnelPane->appendClientTunnelForm(ctc, ui->tunnelsScrollAreaWidgetContents, vdata->tunnelPanes.size(), height); height+=h; //qDebug() << "tun.height:" << height << "sz:" << tunnelPanes.size(); - tunnelPanes.push_back(tunnelPane); - if(name==tunnelNameToFocus){ + vdata->tunnelPanes.push_back(tunnelPane); + if(name.compare(tunnelNameToFocus)==0){ tunnelPane->getNameLineEdit()->setFocus(); ui->tunnelsScrollArea->ensureWidgetVisible(tunnelPane->getNameLineEdit()); } @@ -924,9 +969,12 @@ void MainWindow::appendTunnelForms(std::string tunnelNameToFocus) { QList childWidgets = ui->tunnelsScrollAreaWidgetContents->findChildren(); foreach(QWidget* widget, childWidgets) widget->show(); + ignoreUpdatesOnAppendForms = false; } void MainWindow::deleteTunnelForms() { - for(std::list::iterator it = tunnelPanes.begin(); it != tunnelPanes.end(); ++it) { + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + for(std::list::iterator it = vdata->tunnelPanes.begin(); it != vdata->tunnelPanes.end(); ++it) { TunnelPane* tp = *it; ServerTunnelPane* stp = tp->asServerTunnelPane(); if(stp){ @@ -942,11 +990,13 @@ void MainWindow::deleteTunnelForms() { } throw "unknown TunnelPane subtype"; } - tunnelPanes.clear(); + vdata->tunnelPanes.clear(); } bool MainWindow::applyTunnelsUiToConfigs() { - for(std::list::iterator it = tunnelPanes.begin(); it != tunnelPanes.end(); ++it) { + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); + for(std::list::iterator it = vdata->tunnelPanes.begin(); it != vdata->tunnelPanes.end(); ++it) { TunnelPane* tp = *it; if(!tp->applyDataFromUIToTunnelConfig())return false; } @@ -958,22 +1008,31 @@ void MainWindow::reloadTunnelsConfigAndUI_QString(QString tunnelNameToFocus) { } void MainWindow::reloadTunnelsConfigAndUI(std::string tunnelNameToFocus, QWidget* widgetToFocus) { + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* vdata = volatileDataHolder->getData(); deleteTunnelForms(); - for (std::map::iterator it=tunnelConfigs.begin(); it!=tunnelConfigs.end(); ++it) { - TunnelConfig* tunconf = it->second; + const std::unordered_map tunnelConfigsCopy(vdata->tunnelConfigs); + for (const auto& it : tunnelConfigsCopy) { + TunnelConfig* tunconf = it.second; + vdata->tunnelConfigs[tunconf->getName()]=nullptr; + vdata->tunnelConfigsById[tunconf->get_TunnelId()]=nullptr; delete tunconf; } - tunnelConfigs.clear(); + vdata->tunnelConfigs.clear(); + vdata->tunnelConfigsById.clear(); ReadTunnelsConfig(); appendTunnelForms(tunnelNameToFocus); } void MainWindow::TunnelsPageUpdateListenerMainWindowImpl::updated(std::string oldName, TunnelConfig* tunConf) { - if(oldName!=tunConf->getName()) { + MutexWrapperLock lock(mainWindow->volatileDataHolder->getMutex()); + VolatileData* vdata = mainWindow->volatileDataHolder->getData(); + if(mainWindow->ignoreUpdatesOnAppendForms)return; + if(oldName.compare(tunConf->getName())!=0) { //name has changed - std::map::const_iterator it=mainWindow->tunnelConfigs.find(oldName); - if(it!=mainWindow->tunnelConfigs.end())mainWindow->tunnelConfigs.erase(it); - mainWindow->tunnelConfigs[tunConf->getName()]=tunConf; + auto it=vdata->tunnelConfigs.find(oldName); + if(it!=vdata->tunnelConfigs.end())vdata->tunnelConfigs.erase(it); + vdata->tunnelConfigs[tunConf->getName()]=tunConf; mainWindow->saveAllConfigs(false, FocusEnum::focusOnTunnelName, tunConf->getName()); } else @@ -1153,3 +1212,18 @@ bool MainWindow::isPreventSaveTunnelsMode() { void MainWindow::showTunnelsPagePreventedMessage() { QMessageBox::critical(this,QObject::tr("Error"),QObject::tr("Not saving tunnels configuration due to previous errors with it.")); } + +void MainWindow::DeleteTunnelNamed(std::string name) { + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* tunnels = volatileDataHolder->getData(); + auto it=tunnels->tunnelConfigs.find(name); + if(it!=tunnels->tunnelConfigs.end()){ + TunnelConfig* tc=it->second; + deleteTunnelFromUI(name, tc); + tunnels->tunnelConfigs.erase(it); + tunnels->tunnelConfigsById.erase(tc->get_TunnelId()); + delete tc; + } + saveAllConfigs(true, FocusEnum::noFocus); + delayedSaveManagerPtr->saveNow(); +} diff --git a/src/mainwindow.h b/src/mainwindow.h index 17e85e0..3df6f5d 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -71,11 +71,14 @@ #include "I2pdQtUtil.h" +#include "ConcurrentHolder.h" +#include "MutexWrapperLock.h" + class SaverImpl; class LogViewerManager; -template +/*template bool isType(boost::any& a) { return #ifdef BOOST_AUX_ANY_TYPE_ID_NAME @@ -84,7 +87,17 @@ bool isType(boost::any& a) { a.type() == typeid(ValueType) #endif ; -} +}*/ + +enum TypeEnum { + type_unknown, + type_std_string, + type_bool, + type_uint16, + type_uint32, + type_int, + type_ushort +}; class ConfigOption { public: @@ -117,6 +130,7 @@ public: QString& getRequirementToBeValid() { return requirementToBeValid; } ConfigOption& getConfigOption() { return option; } boost::any optionValue; + TypeEnum optionValueType; virtual ~MainWindowItem(){} virtual void installListeners(MainWindow *mainWindow); virtual void loadFromConfigOption(){ @@ -126,39 +140,43 @@ public: //qDebug() << "loadFromConfigOption[" << optName.c_str() << "]"; boost::any programOption; i2p::config::GetOptionAsAny(optName, programOption); - optionValue=programOption.empty()?boost::any(std::string("")) - :boost::any_cast(programOption).value(); + if(programOption.empty()){ + optionValue=boost::any(std::string("")); + optionValueType=type_std_string; + }else{ + optionValue=boost::any_cast(programOption).value(); + } } virtual void saveToStringStream(std::stringstream& out){ if(readOnly)return; //should readOnly items (conf=) error somewhere, instead of silently skipping save? - if(isType(optionValue)) { - std::string v = boost::any_cast(optionValue); + if(optionValueType==type_std_string) { + const std::string v = boost::any_cast(optionValue); if(v.empty())return; } if(optionValue.empty())return; - std::string rtti = optionValue.type().name(); + //std::string rtti = optionValue.type().name(); std::string optName=""; if(!option.section.isEmpty())optName=option.section.toStdString()+std::string("."); - optName+=option.option.toStdString(); + optName+=std::string(option.option.toStdString()); //qDebug() << "Writing option" << optName.c_str() << "of type" << rtti.c_str(); - std::string sectionAsStdStr = option.section.toStdString(); + std::string sectionAsStdStr = std::string(option.section.toStdString()); if(!option.section.isEmpty() && sectionAsStdStr!=programOptionsWriterCurrentSection) { out << "[" << sectionAsStdStr << "]\n"; programOptionsWriterCurrentSection=sectionAsStdStr; } - out << option.option.toStdString() << "="; - if(isType(optionValue)) { - out << boost::any_cast(optionValue); - }else if(isType(optionValue)) { + out << std::string(option.option.toStdString()) << "="; + if(type_std_string==optionValueType) { + out << boost::any_cast(optionValue); + }else if(type_bool==optionValueType) { out << (boost::any_cast(optionValue) ? "true" : "false"); - }else if(isType(optionValue)) { + }else if(type_uint16==optionValueType) { out << boost::any_cast(optionValue); - }else if(isType(optionValue)) { + }else if(type_uint32==optionValueType) { out << boost::any_cast(optionValue); - }else if(isType(optionValue)) { + }else if(type_int==optionValueType) { out << boost::any_cast(optionValue); - }else if(isType(optionValue)) { + }else if(type_ushort==optionValueType) { out << boost::any_cast(optionValue); }else out << boost::any_cast(optionValue); //let it throw out << "\n\n"; @@ -194,9 +212,11 @@ public: virtual void saveToStringStream(std::stringstream& out){ optionValue=fromString(lineEdit->text()); + optionValueType=getOptionValueType(); MainWindowItem::saveToStringStream(out); } virtual bool isValid(bool & alreadyDisplayedIfWrong); + virtual TypeEnum getOptionValueType() {return type_std_string;} }; class FileOrFolderChooserItem : public BaseStringItem { protected: @@ -206,6 +226,7 @@ public: FileOrFolderChooserItem(ConfigOption option_, QLineEdit* lineEdit_, QPushButton* browsePushButton_, MainWindow* mw, bool requireExistingFile_, bool readOnly) : BaseStringItem(option_, lineEdit_, QString(), mw, readOnly), requireExistingFile(requireExistingFile_), browsePushButton(browsePushButton_) {} virtual ~FileOrFolderChooserItem(){} + TypeEnum getOptionValueType() {return type_std_string;} }; class FileChooserItem : public FileOrFolderChooserItem { Q_OBJECT @@ -247,11 +268,13 @@ public: comboBox->setCurrentText(QString(boost::any_cast(optionValue).c_str())); } virtual void saveToStringStream(std::stringstream& out){ - std::string logDest = comboBox->currentText().toStdString(); + const std::string logDest = std::string(comboBox->currentText().toStdString()); optionValue=logDest; + optionValueType=type_std_string; MainWindowItem::saveToStringStream(out); } //virtual bool isValid(bool & alreadyDisplayedIfWrong) { return true; } + TypeEnum getOptionValueType() {return type_std_string;} Q_OBJECT }; @@ -265,8 +288,10 @@ public: } virtual void saveToStringStream(std::stringstream& out){ optionValue=comboBox->currentText().toStdString(); + optionValueType=type_std_string; MainWindowItem::saveToStringStream(out); } + TypeEnum getOptionValueType() {return type_std_string;} //virtual bool isValid(bool & alreadyDisplayedIfWrong) { return true; } }; class SignatureTypeComboBoxItem : public ComboBoxItem { @@ -282,9 +307,11 @@ public: virtual void saveToStringStream(std::stringstream& out){ uint16_t selected = SignatureTypeComboBoxFactory::getSigType(comboBox->currentData()); optionValue=(unsigned short)selected; + optionValueType=type_ushort; MainWindowItem::saveToStringStream(out); } //virtual bool isValid(bool & alreadyDisplayedIfWrong) { return true; } + TypeEnum getOptionValueType() {return type_ushort;} }; class CheckBoxItem : public MainWindowItem { public: @@ -298,10 +325,12 @@ public: checkBox->setChecked(boost::any_cast(optionValue)); } virtual void saveToStringStream(std::stringstream& out){ - optionValue=checkBox->isChecked(); + optionValue=static_cast(checkBox->isChecked()); + optionValueType=type_bool; MainWindowItem::saveToStringStream(out); } //virtual bool isValid(bool & alreadyDisplayedIfWrong) { return true; } + TypeEnum getOptionValueType() {return type_bool;} }; class BaseFormattedStringItem : public BaseStringItem { public: @@ -310,6 +339,7 @@ public: BaseStringItem(option_, lineEdit_, requirementToBeValid_, mw), fieldNameTranslated(fieldNameTranslated_) {} virtual ~BaseFormattedStringItem(){} //virtual bool isValid(bool & alreadyDisplayedIfWrong)=0; + TypeEnum getOptionValueType() {return type_std_string;} }; class IntegerStringItem : public BaseFormattedStringItem { public: @@ -327,6 +357,7 @@ public: } virtual QString toString(){return QString::number(boost::any_cast(optionValue));} virtual boost::any fromString(QString s){return boost::any(std::stoi(s.toStdString()));} + TypeEnum getOptionValueType() {return type_int;} }; class UShortStringItem : public BaseFormattedStringItem { public: @@ -344,6 +375,7 @@ public: } virtual QString toString(){return QString::number(boost::any_cast(optionValue));} virtual boost::any fromString(QString s){return boost::any((unsigned short)std::stoi(s.toStdString()));} + TypeEnum getOptionValueType() {return type_ushort;} }; class UInt32StringItem : public BaseFormattedStringItem { public: @@ -361,6 +393,7 @@ public: } virtual QString toString(){return QString::number(boost::any_cast(optionValue));} virtual boost::any fromString(QString s){return boost::any((uint32_t)std::stoi(s.toStdString()));} + TypeEnum getOptionValueType() {return type_uint32;} }; class UInt16StringItem : public BaseFormattedStringItem { public: @@ -378,12 +411,14 @@ public: } virtual QString toString(){return QString::number(boost::any_cast(optionValue));} virtual boost::any fromString(QString s){return boost::any((uint16_t)std::stoi(s.toStdString()));} + TypeEnum getOptionValueType() {return type_uint16;} }; class IPAddressStringItem : public BaseFormattedStringItem { public: IPAddressStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, MainWindow* mw) : BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be an IPv4 address"), mw) {} //virtual bool isValid(bool & alreadyDisplayedIfWrong){return true;}//todo + TypeEnum getOptionValueType() {return type_std_string;} }; class TCPPortStringItem : public UShortStringItem { public: @@ -525,7 +560,6 @@ protected: QString getStatusPageHtml(bool showHiddenInfo); - QList configItems; NonGUIOptionItem* daemonOption; NonGUIOptionItem* serviceOption; //LogDestinationComboBoxItem* logOption; @@ -544,7 +578,7 @@ protected: void initUInt32Box(ConfigOption option, QLineEdit* numberLineEdit, QString fieldNameTranslated); void initUInt16Box(ConfigOption option, QLineEdit* numberLineEdit, QString fieldNameTranslated); void initStringBox(ConfigOption option, QLineEdit* lineEdit); - NonGUIOptionItem* initNonGUIOption(ConfigOption option); + NonGUIOptionItem* initNonGUIOption(ConfigOption option, TypeEnum optionValueType); void loadAllConfigs(SaverImpl* saverPtr); void layoutTunnels(); @@ -569,11 +603,20 @@ private: QString datadir; QString confpath; QString tunconfpath; + bool ignoreUpdatesOnAppendForms; + +public: + struct VolatileData { + QList configItems; + std::unordered_map tunnelConfigs; + std::unordered_map tunnelConfigsById; + std::list tunnelPanes; + VolatileData():configItems(), tunnelConfigs(), tunnelConfigsById(), tunnelPanes() {} + }; - std::map tunnelConfigs; - std::map tunnelConfigsById; - std::list tunnelPanes; + ConcurrentHolder* volatileDataHolder; +private: void appendTunnelForms(std::string tunnelNameToFocus); void deleteTunnelForms(); void deleteTunnelFromUI(std::string tunnelName, TunnelConfig* cnf); @@ -653,40 +696,34 @@ private: param.set_i2p_streaming_initialAckDelay(QString::number(_i2p_streaming_initialAckDelay)); } - - void DeleteTunnelNamed(std::string name) { - std::map::const_iterator it=tunnelConfigs.find(name); - if(it!=tunnelConfigs.end()){ - TunnelConfig* tc=it->second; - deleteTunnelFromUI(name, tc); - tunnelConfigs.erase(it); - tunnelConfigsById.erase(tc->get_TunnelId()); - delete tc; - } - saveAllConfigs(true, FocusEnum::noFocus); - delayedSaveManagerPtr->saveNow(); - } + void DeleteTunnelNamed(std::string name); std::string GenerateNewTunnelName() { + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* tunnels = volatileDataHolder->getData(); int i=1; while(true){ std::stringstream name; name << "name" << i; const std::string& str=name.str(); - if(tunnelConfigs.find(str)==tunnelConfigs.end())return str; + if(tunnels->tunnelConfigs.find(str)==tunnels->tunnelConfigs.end())return str; ++i; } } int GenerateNewTunnelId() { + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* tunnels = volatileDataHolder->getData(); int i=1; while(true){ - if(tunnelConfigsById.find(i)==tunnelConfigsById.end())return i; + if(tunnels->tunnelConfigsById.find(i)==tunnels->tunnelConfigsById.end())return i; ++i; } } void CreateDefaultClientTunnel() {//TODO dedup default values with ReadTunnelsConfig() and with ClientContext.cpp::ReadTunnels () + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* tunnels = volatileDataHolder->getData(); int tunnelId=GenerateNewTunnelId(); std::string name=GenerateNewTunnelName(); std::string type = I2P_TUNNELS_SECTION_TYPE_CLIENT; @@ -701,7 +738,7 @@ private: I2CPParameters i2cpParameters; CreateDefaultI2CPOptions (i2cpParameters); - tunnelConfigs[name]=tunnelConfigsById[tunnelId]=new ClientTunnelConfig( + tunnels->tunnelConfigs[name]=tunnels->tunnelConfigsById[tunnelId]=new ClientTunnelConfig( tunnelId, name, QString(type.c_str()), i2cpParameters, dest, @@ -717,6 +754,8 @@ private: } void CreateDefaultServerTunnel() {//TODO dedup default values with ReadTunnelsConfig() and with ClientContext.cpp::ReadTunnels () + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* tunnels = volatileDataHolder->getData(); int tunnelId=GenerateNewTunnelId(); std::string name=GenerateNewTunnelName(); std::string type=I2P_TUNNELS_SECTION_TYPE_SERVER; @@ -737,7 +776,7 @@ private: I2CPParameters i2cpParameters; CreateDefaultI2CPOptions (i2cpParameters); - tunnelConfigs[name]=tunnelConfigsById[tunnelId]=new ServerTunnelConfig( + tunnels->tunnelConfigs[name]=tunnels->tunnelConfigsById[tunnelId]=new ServerTunnelConfig( tunnelId, name, QString(type.c_str()), i2cpParameters, host, @@ -760,6 +799,9 @@ private: void ReadTunnelsConfig() //TODO deduplicate the code with ClientContext.cpp::ReadTunnels () { + MutexWrapperLock lock(volatileDataHolder->getMutex()); + VolatileData* tunnels = volatileDataHolder->getData(); + boost::property_tree::ptree pt; std::string tunConf=tunconfpath.toStdString(); if (tunConf == "") { @@ -816,7 +858,7 @@ private: ReadI2CPOptions (section, options, i2cpParameters); int tunnelId=GenerateNewTunnelId(); - tunnelConfigs[name]=tunnelConfigsById[tunnelId]= + tunnels->tunnelConfigs[name]=tunnels->tunnelConfigsById[tunnelId]= new ClientTunnelConfig(tunnelId,name, QString(type.c_str()), i2cpParameters, dest, port, @@ -868,7 +910,7 @@ private: while (comma != std::string::npos); } */ - tunnelConfigs[name]=tunnelConfigsById[tunnelId]= + tunnels->tunnelConfigs[name]=tunnels->tunnelConfigsById[tunnelId]= new ServerTunnelConfig(tunnelId,name, QString(type.c_str()), i2cpParameters, host, port,