From 1088c0a933b18c858ca826ccdd3046864a2518a4 Mon Sep 17 00:00:00 2001 From: Peter L Jones Date: Fri, 29 Oct 2021 22:55:41 +0100 Subject: [PATCH] UI: Amend server registration, add server persistence --- src/main.cpp | 110 ++++++------ src/server.cpp | 6 - src/server.h | 85 ++++------ src/serverdlg.cpp | 267 ++++++++++++++++------------- src/serverdlg.h | 42 +++-- src/serverdlgbase.ui | 39 +++-- src/serverlist.cpp | 388 +++++++++++++++++++++++++++++-------------- src/serverlist.h | 59 +++---- src/settings.cpp | 141 ++++++++++------ src/util.h | 14 +- 10 files changed, 678 insertions(+), 473 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index e63300c678..3ddc8cf4ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -557,6 +557,9 @@ int main ( int argc, char** argv ) } #endif + // TODO create settings in default state, if loading from file do that next, then come back here to + // override from command line options, then create client or server, letting them do the validation + if ( bIsClient ) { if ( ServerOnlyOptions.size() != 0 ) @@ -593,65 +596,32 @@ int main ( int argc, char** argv ) if ( bUseGUI ) { - if ( strDirectoryServer.isEmpty() ) - { - // per definition: if we are in "GUI" server mode and no directory server - // address is given, we use the default directory server address - strDirectoryServer = DEFAULT_SERVER_ADDRESS; - qInfo() << qUtf8Printable ( QString ( "- default directory server set: %1" ).arg ( strDirectoryServer ) ); - } - } - else - { - // the inifile is not supported for the headless server mode - if ( !strIniFileName.isEmpty() ) - { - qWarning() << "No initialization file support in headless server mode."; - } - } - - if ( strDirectoryServer.isEmpty() ) - { - // per definition, we must be a headless server and ignoring inifile, so we have all the information + // by definition, when running with the GUI we always default to registering somewhere but + // until the settings are loaded we do not know where, so we cannot be prescriptive here if ( !strServerListFileName.isEmpty() ) { - qWarning() << "Server list persistence file will only take effect when running as a directory server."; - strServerListFileName = ""; + qInfo() << "Note:" + << "Server list persistence file will only take effect when running as a directory server."; } if ( !strServerListFilter.isEmpty() ) { - qWarning() << "Server list filter will only take effect when running as a directory server."; - strServerListFilter = ""; - } - - if ( !strServerPublicIP.isEmpty() ) - { - qWarning() << "Server Public IP will only take effect when registering a server with a directory server."; - strServerPublicIP = ""; + qInfo() << "Note:" + << "Server list filter will only take effect when running as a directory server."; } } else { - // either we are not headless and there is an inifile, or a directory server was supplied on the command line - - // if we are not headless, certain checks cannot be made, as the inifile state is not yet known - if ( !bUseGUI && strDirectoryServer.compare ( "localhost", Qt::CaseInsensitive ) != 0 && strDirectoryServer.compare ( "127.0.0.1" ) != 0 ) + // the inifile is not supported for the headless server mode + if ( !strIniFileName.isEmpty() ) { - if ( !strServerListFileName.isEmpty() ) - { - qWarning() << "Server list persistence file will only take effect when running as a directory server."; - strServerListFileName = ""; - } - - if ( !strServerListFilter.isEmpty() ) - { - qWarning() << "Server list filter will only take effect when running as a directory server."; - strServerListFileName = ""; - } + qWarning() << "No initialization file support in headless server mode."; + strIniFileName = ""; } - else + // therefore we know everything based on command line options + + if ( strDirectoryServer.compare ( "localhost", Qt::CaseInsensitive ) == 0 || strDirectoryServer.compare ( "127.0.0.1" ) == 0 ) { if ( !strServerListFileName.isEmpty() ) { @@ -709,16 +679,41 @@ int main ( int argc, char** argv ) } } } + else + { + if ( !strServerListFileName.isEmpty() ) + { + qWarning() << "Server list persistence file will only take effect when running as a directory server."; + strServerListFileName = ""; + } + + if ( !strServerListFilter.isEmpty() ) + { + qWarning() << "Server list filter will only take effect when running as a directory server."; + strServerListFileName = ""; + } + } - if ( !strServerPublicIP.isEmpty() ) + if ( strDirectoryServer.isEmpty() ) { - QHostAddress InetAddr; - if ( !InetAddr.setAddress ( strServerPublicIP ) ) + if ( !strServerPublicIP.isEmpty() ) { - qWarning() << "Server Public IP is invalid. Only plain IP addresses are supported."; + qWarning() << "Server Public IP will only take effect when registering a server with a directory server."; strServerPublicIP = ""; } } + else + { + if ( !strServerPublicIP.isEmpty() ) + { + QHostAddress InetAddr; + if ( !InetAddr.setAddress ( strServerPublicIP ) ) + { + qWarning() << "Server Public IP is invalid. Only plain IP addresses are supported."; + strServerPublicIP = ""; + } + } + } } if ( !strServerBindIP.isEmpty() ) @@ -881,10 +876,6 @@ int main ( int argc, char** argv ) CLocale::LoadTranslation ( Settings.strLanguage, pApp ); } - // update server list AFTER restoring the settings from the - // settings file - Server.UpdateServerList(); - // GUI object for the server CServerDlg ServerDlg ( &Server, &Settings, bStartMinimized, nullptr ); @@ -902,11 +893,12 @@ int main ( int argc, char** argv ) // only start application without using the GUI qInfo() << qUtf8Printable ( GetVersionAndNameStr ( false ) ); - // enable server list if a directory server is defined - Server.SetServerRegistered ( !strDirectoryServer.isEmpty() ); - - // update serverlist - Server.UpdateServerList(); + // CServerListManager defaults to AT_NONE, so need to switch if + // strDirectoryServer is wanted + if ( !strDirectoryServer.isEmpty() ) + { + Server.SetDirectoryType ( AT_CUSTOM ); + } pApp->exec(); } diff --git a/src/server.cpp b/src/server.cpp index a979a37d2a..9bc157ae87 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -684,12 +684,6 @@ void CServer::OnAboutToQuit() Stop(); - // if server was registered at the directory server, unregister on shutdown - if ( GetServerRegistered() ) - { - Unregister(); - } - if ( bWriteStatusHTMLFile ) { WriteHTMLServerQuit(); diff --git a/src/server.h b/src/server.h index 1636f733e8..a90c64b47e 100644 --- a/src/server.h +++ b/src/server.h @@ -190,70 +190,50 @@ class CServer : public QObject, public CServerSlots void CreateCLServerListReqVerAndOSMes ( const CHostAddress& InetAddr ) { ConnLessProtocol.CreateCLReqVersionAndOSMes ( InetAddr ); } - // Jam recorder ------------------------------------------------------------ + // IPv6 Enabled + bool IsIPv6Enabled() { return bEnableIPv6; } + + // GUI settings ------------------------------------------------------------ + int GetClientNumAudioChannels ( const int iChanNum ) { return vecChannels[iChanNum].GetNumAudioChannels(); } + + void SetDirectoryType ( const EDirectoryType eNCSAT ) { ServerListManager.SetDirectoryType ( eNCSAT ); } + EDirectoryType GetDirectoryType() { return ServerListManager.GetDirectoryType(); } + bool IsDirectoryServer() { return ServerListManager.IsDirectoryServer(); } + ESvrRegStatus GetSvrRegStatus() { return ServerListManager.GetSvrRegStatus(); } + + void SetServerName ( const QString& strNewName ) { ServerListManager.SetServerName ( strNewName ); } + QString GetServerName() { return ServerListManager.GetServerName(); } + void SetServerCity ( const QString& strNewCity ) { ServerListManager.SetServerCity ( strNewCity ); } + QString GetServerCity() { return ServerListManager.GetServerCity(); } + void SetServerCountry ( const QLocale::Country eNewCountry ) { ServerListManager.SetServerCountry ( eNewCountry ); } + QLocale::Country GetServerCountry() { return ServerListManager.GetServerCountry(); } + bool GetRecorderInitialised() { return JamController.GetRecorderInitialised(); } + void SetEnableRecording ( bool bNewEnableRecording ); + bool GetDisableRecording() { return bDisableRecording; } QString GetRecorderErrMsg() { return JamController.GetRecorderErrMsg(); } bool GetRecordingEnabled() { return JamController.GetRecordingEnabled(); } - bool GetDisableRecording() { return bDisableRecording; } void RequestNewRecording() { JamController.RequestNewRecording(); } - - void SetEnableRecording ( bool bNewEnableRecording ); - - QString GetRecordingDir() { return JamController.GetRecordingDir(); } - - void SetRecordingDir ( QString newRecordingDir ) + void SetRecordingDir ( QString newRecordingDir ) { JamController.SetRecordingDir ( newRecordingDir, iServerFrameSizeSamples, bDisableRecording ); } - - void CreateAndSendRecorderStateForAllConChannels(); - - // delay panning - void SetEnableDelayPanning ( bool bDelayPanningOn ) { bDelayPan = bDelayPanningOn; } - bool IsDelayPanningEnabled() { return bDelayPan; } - - // IPv6 Enabled - bool IsIPv6Enabled() { return bEnableIPv6; } - - // Server list management -------------------------------------------------- - void UpdateServerList() { ServerListManager.Update(); } - - void Unregister() { ServerListManager.Unregister(); } - - void SetServerRegistered ( const bool bState ) { ServerListManager.SetEnabled ( bState ); } - - bool GetServerRegistered() { return ServerListManager.GetEnabled(); } - - void SetDirectoryAddress ( const QString& sNDirectoryAddress ) { ServerListManager.SetDirectoryAddress ( sNDirectoryAddress ); } - - QString GetDirectoryAddress() { return ServerListManager.GetDirectoryAddress(); } - - void SetDirectoryType ( const EDirectoryType eNCSAT ) { ServerListManager.SetDirectoryType ( eNCSAT ); } - - EDirectoryType GetDirectoryType() { return ServerListManager.GetDirectoryType(); } - - void SetServerName ( const QString& strNewName ) { ServerListManager.SetServerName ( strNewName ); } - - QString GetServerName() { return ServerListManager.GetServerName(); } - - void SetServerCity ( const QString& strNewCity ) { ServerListManager.SetServerCity ( strNewCity ); } - - QString GetServerCity() { return ServerListManager.GetServerCity(); } - - void SetServerCountry ( const QLocale::Country eNewCountry ) { ServerListManager.SetServerCountry ( eNewCountry ); } - - QLocale::Country GetServerCountry() { return ServerListManager.GetServerCountry(); } + QString GetRecordingDir() { return JamController.GetRecordingDir(); } void SetWelcomeMessage ( const QString& strNWelcMess ); QString GetWelcomeMessage() { return strWelcomeMessage; } - ESvrRegStatus GetSvrRegStatus() { return ServerListManager.GetSvrRegStatus(); } + void SetDirectoryAddress ( const QString& sNDirectoryAddress ) { ServerListManager.SetDirectoryAddress ( sNDirectoryAddress ); } + QString GetDirectoryAddress() { return ServerListManager.GetDirectoryAddress(); } + + QString GetServerListFileName() { return ServerListManager.GetServerListFileName(); } + bool SetServerListFileName ( QString strFilename ) { return ServerListManager.SetServerListFileName ( strFilename ); } - // GUI settings ------------------------------------------------------------ void SetAutoRunMinimized ( const bool NAuRuMin ) { bAutoRunMinimized = NAuRuMin; } bool GetAutoRunMinimized() { return bAutoRunMinimized; } - int GetClientNumAudioChannels ( const int iChanNum ) { return vecChannels[iChanNum].GetNumAudioChannels(); } + void SetEnableDelayPanning ( bool bDelayPanningOn ) { bDelayPan = bDelayPanningOn; } + bool IsDelayPanningEnabled() { return bDelayPan; } protected: // access functions for actual channels @@ -292,6 +272,8 @@ class CServer : public QObject, public CServerSlots virtual void customEvent ( QEvent* pEvent ); + void CreateAndSendRecorderStateForAllConChannels(); + // if server mode is normal or double system frame size bool bUseDoubleSystemFrameSize; int iServerFrameSizeSamples; @@ -433,9 +415,8 @@ public slots: void OnCLSendEmptyMes ( CHostAddress TargetInetAddr ) { - // only send empty message if server list is enabled and this is not - // a directory server - if ( ServerListManager.GetEnabled() && !ServerListManager.IsDirectoryServer() ) + // only send empty message if not a directory server + if ( !ServerListManager.IsDirectoryServer() ) { ConnLessProtocol.CreateCLEmptyMes ( TargetInetAddr ); } diff --git a/src/serverdlg.cpp b/src/serverdlg.cpp index 92b7bbad9b..1fce7e5a10 100644 --- a/src/serverdlg.cpp +++ b/src/serverdlg.cpp @@ -52,24 +52,36 @@ CServerDlg::CServerDlg ( CServer* pNServP, CServerSettings* pNSetP, const bool b lvwClients->setAccessibleName ( tr ( "Connected clients list view" ) ); - // Make My Server Public flag - chbRegisterServer->setWhatsThis ( "" + tr ( "Make My Server Public" ) + ": " + - tr ( "If the Make My Server Public check box is checked, this server registers " - "itself at the directory server so that all users of the application " - "can see the server in the connect dialog server list and " - "connect to it. The registration of the server is renewed periodically " - "to make sure that all servers in the connect dialog server list are " - "actually available." ) ); + // Server list selection combo box + QString strDirectoryTypeAN = tr ( "Directory Type combo box" ); + lblDirectoryType->setAccessibleName ( strDirectoryTypeAN ); + cbxDirectoryType->setAccessibleName ( strDirectoryTypeAN ); + + QString strDirectoryTypeWT = "" + tr ( "Directory" ) + ": " + + tr ( "Select '%1' not to register your server with a directory." ).arg ( DirectoryTypeToString ( AT_NONE ) ) + + "
" + tr ( "Select one of the genres to register with that directory." ) + "
" + + tr ( "Or select '%1' and specify a Custom Directory address on the " + "Options tab to register with a custom directory." ) + .arg ( DirectoryTypeToString ( AT_CUSTOM ) ) + + "

" + + tr ( "For any value except '%1', this server registers " + "with a directory so that a %2 user can select this server " + "in the client connect dialog server list when they choose that directory." ) + .arg ( DirectoryTypeToString ( AT_NONE ) ) + .arg ( APP_NAME ) + + "

" + + tr ( "The registration of the server is renewed periodically " + "to make sure that all servers in the connect dialog server list are " + "actually available." ); + lblDirectoryType->setWhatsThis ( strDirectoryTypeWT ); + cbxDirectoryType->setWhatsThis ( strDirectoryTypeWT ); // server registration status label lblRegSvrStatus->setWhatsThis ( "" + tr ( "Register Server Status" ) + ": " + - tr ( "If the Register Server check box is checked, this will show " - "whether registration with the directory server is successful. If the " - "registration failed, please choose another server list." ) ); - - cbxDirectoryType->setWhatsThis ( "" + tr ( "Server List Selection" ) + ": " + - tr ( "Selects the server list (i.e. directory server address) in which your server will be added." ) ); - cbxDirectoryType->setAccessibleName ( tr ( "Server list selection combo box" ) ); + tr ( "When a value other than \"%1\" is chosen for Directory, this will show " + "whether registration is successful. If the registration failed, " + "please choose a different directory." ) + .arg ( DirectoryTypeToString ( AT_NONE ) ) ); // server name QString strServName = "" + tr ( "Server Name" ) + ": " + @@ -171,14 +183,35 @@ CServerDlg::CServerDlg ( CServer* pNServP, CServerSettings* pNSetP, const bool b "This will prevent recording until a new value is selected." ) ); // custom directory - QString strCustomDirectory = "" + tr ( "Custom Directory" ) + ": " + - tr ( "The custom directory is the IP address or URL of the directory " - "server at which the server list of the connection dialog is managed." ); + QString strCustomDirectory = "" + tr ( "Custom Directory address" ) + ": " + + tr ( "The Custom Directory address is the address of the directory " + "holding the server list to which this server should be added." ); lblCustomDirectory->setWhatsThis ( strCustomDirectory ); edtCustomDirectory->setWhatsThis ( strCustomDirectory ); edtCustomDirectory->setAccessibleName ( tr ( "Custom Directory line edit" ) ); + // server list persistence file name (directory server only) + pbtServerListPersistence->setAccessibleName ( tr ( "Server List Filename dialog push button" ) ); + pbtServerListPersistence->setWhatsThis ( "" + tr ( "Server List Filename" ) + ": " + + tr ( "Click the button to open the dialog that allows the " + "server list persistence file name to be set. The user Jamulus is running as " + "needs to be able to create the file name specified " + "although it may already exist (it will get overwritten on save)." ) ); + + edtServerListPersistence->setAccessibleName ( tr ( "Server List Filename text box (read-only)" ) ); + edtServerListPersistence->setWhatsThis ( "" + tr ( "Server List Filename" ) + ": " + + tr ( "The current value of server list persistence file name. The user Jamulus is running as " + "needs to be able to create the file name specified " + "although it may already exist (it will get overwritten on save). " + "Click the button to open the dialog that allows the " + "server list persistence file name to be set." ) ); + + tbtClearServerListPersistence->setAccessibleName ( tr ( "Clear the server list file name button" ) ); + tbtClearServerListPersistence->setWhatsThis ( "" + tr ( "Clear Server List Filename" ) + ": " + + tr ( "Click the button to clear the currently selected server list persistence file name. " + "This will prevent persisting the server list until a new value is selected." ) ); + // start minimized on operating system start chbStartOnOSStart->setWhatsThis ( "" + tr ( "Start Minimized on Operating System Start" ) + ": " + tr ( "If the start minimized on operating system start " @@ -247,6 +280,7 @@ lvwClients->setMinimumHeight ( 140 ); // directory type combo box cbxDirectoryType->clear(); + cbxDirectoryType->addItem ( DirectoryTypeToString ( AT_NONE ) ); cbxDirectoryType->addItem ( DirectoryTypeToString ( AT_DEFAULT ) ); cbxDirectoryType->addItem ( DirectoryTypeToString ( AT_ANY_GENRE2 ) ); cbxDirectoryType->addItem ( DirectoryTypeToString ( AT_ANY_GENRE3 ) ); @@ -255,16 +289,10 @@ lvwClients->setMinimumHeight ( 140 ); cbxDirectoryType->addItem ( DirectoryTypeToString ( AT_GENRE_CLASSICAL_FOLK ) ); cbxDirectoryType->addItem ( DirectoryTypeToString ( AT_GENRE_CHORAL ) ); cbxDirectoryType->addItem ( DirectoryTypeToString ( AT_CUSTOM ) ); - cbxDirectoryType->setCurrentIndex ( static_cast ( pServer->GetDirectoryType() ) ); - - // custom directory server address - edtCustomDirectory->setText ( pServer->GetDirectoryAddress() ); - // update server name line edit - edtServerName->setText ( pServer->GetServerName() ); - - // update server city line edit - edtLocationCity->setText ( pServer->GetServerCity() ); + // server info max lengths + edtServerName->setMaxLength ( MAX_LEN_SERVER_NAME ); + edtLocationCity->setMaxLength ( MAX_LEN_SERVER_CITY ); // load country combo box with all available countries cbxLocationCountry->setInsertPolicy ( QComboBox::NoInsert ); @@ -284,36 +312,20 @@ lvwClients->setMinimumHeight ( 140 ); // sort country combo box items in alphabetical order cbxLocationCountry->model()->sort ( 0, Qt::AscendingOrder ); - // select current country - cbxLocationCountry->setCurrentIndex ( cbxLocationCountry->findData ( static_cast ( pServer->GetServerCountry() ) ) ); - - // update register server check box - if ( pServer->GetServerRegistered() ) - { - chbRegisterServer->setCheckState ( Qt::Checked ); - } - else - { - chbRegisterServer->setCheckState ( Qt::Unchecked ); - } - - // Recorder controls - chbEnableRecorder->setChecked ( pServer->GetRecordingEnabled() ); - edtCurrentSessionDir->setText ( "" ); - pbtNewRecording->setAutoDefault ( false ); - // setup welcome message GUI control tedWelcomeMessage->setPlaceholderText ( tr ( "Type a message here. If no message is set, the server welcome is disabled." ) ); - tedWelcomeMessage->setText ( pServer->GetWelcomeMessage() ); // language combo box (corrects the setting if language not found) cbxLanguage->Init ( pSettings->strLanguage ); // recorder options pbtRecordingDir->setAutoDefault ( false ); - edtRecordingDir->setText ( pServer->GetRecordingDir() ); tbtClearRecordingDir->setText ( u8"\u232B" ); + // server list persistence file + pbtServerListPersistence->setAutoDefault ( false ); + tbtClearServerListPersistence->setText ( u8"\u232B" ); + // update start minimized check box (only available for Windows) #ifndef _WIN32 chbStartOnOSStart->setVisible ( false ); @@ -377,8 +389,6 @@ lvwClients->setMinimumHeight ( 140 ); // Connections ------------------------------------------------------------- // check boxes - QObject::connect ( chbRegisterServer, &QCheckBox::stateChanged, this, &CServerDlg::OnRegisterServerStateChanged ); - QObject::connect ( chbEnableRecorder, &QCheckBox::stateChanged, this, &CServerDlg::OnEnableRecorderStateChanged ); QObject::connect ( chbStartOnOSStart, &QCheckBox::stateChanged, this, &CServerDlg::OnStartOnOSStartStateChanged ); @@ -386,22 +396,22 @@ lvwClients->setMinimumHeight ( 140 ); QObject::connect ( chbEnableDelayPanning, &QCheckBox::stateChanged, this, &CServerDlg::OnEnableDelayPanningStateChanged ); // line edits - QObject::connect ( edtServerName, &QLineEdit::textChanged, this, &CServerDlg::OnServerNameTextChanged ); + QObject::connect ( edtServerName, &QLineEdit::editingFinished, this, &CServerDlg::OnServerNameEditingFinished ); - QObject::connect ( edtLocationCity, &QLineEdit::textChanged, this, &CServerDlg::OnLocationCityTextChanged ); + QObject::connect ( edtLocationCity, &QLineEdit::editingFinished, this, &CServerDlg::OnLocationCityEditingFinished ); QObject::connect ( edtCustomDirectory, &QLineEdit::editingFinished, this, &CServerDlg::OnCustomDirectoryEditingFinished ); // combo boxes QObject::connect ( cbxDirectoryType, - static_cast ( &QComboBox::activated ), + static_cast ( &QComboBox::currentIndexChanged ), this, - &CServerDlg::OnDirectoryTypeActivated ); + &CServerDlg::OnDirectoryTypeCurrentIndexChanged ); QObject::connect ( cbxLocationCountry, - static_cast ( &QComboBox::activated ), + static_cast ( &QComboBox::currentIndexChanged ), this, - &CServerDlg::OnLocationCountryActivated ); + &CServerDlg::OnLocationCountryCurrentIndexChanged ); QObject::connect ( cbxLanguage, &CLanguageComboBox::LanguageChanged, this, &CServerDlg::OnLanguageChanged ); @@ -410,9 +420,13 @@ lvwClients->setMinimumHeight ( 140 ); QObject::connect ( pbtRecordingDir, &QPushButton::released, this, &CServerDlg::OnRecordingDirClicked ); + QObject::connect ( pbtServerListPersistence, &QPushButton::released, this, &CServerDlg::OnServerListPersistenceClicked ); + // tool buttons QObject::connect ( tbtClearRecordingDir, &QToolButton::released, this, &CServerDlg::OnClearRecordingDirClicked ); + QObject::connect ( tbtClearServerListPersistence, &QToolButton::released, this, &CServerDlg::OnClearServerListPersistenceClicked ); + // timers QObject::connect ( &Timer, &QTimer::timeout, this, &CServerDlg::OnTimer ); @@ -475,41 +489,15 @@ void CServerDlg::OnStartOnOSStartStateChanged ( int value ) ModifyAutoStartEntry ( bCurAutoStartMinState ); } -void CServerDlg::OnRegisterServerStateChanged ( int value ) +void CServerDlg::OnServerNameEditingFinished() { - const bool bRegState = ( value == Qt::Checked ); + QString strNewName = edtServerName->text(); - // apply new setting to the server and update it - pServer->SetServerRegistered ( bRegState ); - - // if registering is disabled, unregister server - if ( !bRegState ) - { - pServer->Unregister(); - } - - pServer->UpdateServerList(); - - // update GUI dependencies - UpdateGUIDependencies(); -} - -void CServerDlg::OnCustomDirectoryEditingFinished() -{ - // apply new setting to the server and update it - pServer->SetDirectoryAddress ( edtCustomDirectory->text() ); - - pServer->UpdateServerList(); -} - -void CServerDlg::OnServerNameTextChanged ( const QString& strNewName ) -{ // check length if ( strNewName.length() <= MAX_LEN_SERVER_NAME ) { // apply new setting to the server and update it pServer->SetServerName ( strNewName ); - pServer->UpdateServerList(); } else { @@ -518,14 +506,15 @@ void CServerDlg::OnServerNameTextChanged ( const QString& strNewName ) } } -void CServerDlg::OnLocationCityTextChanged ( const QString& strNewCity ) +void CServerDlg::OnLocationCityEditingFinished() { + QString strNewCity = edtLocationCity->text(); + // check length if ( strNewCity.length() <= MAX_LEN_SERVER_CITY ) { // apply new setting to the server and update it pServer->SetServerCity ( strNewCity ); - pServer->UpdateServerList(); } else { @@ -534,20 +523,25 @@ void CServerDlg::OnLocationCityTextChanged ( const QString& strNewCity ) } } -void CServerDlg::OnLocationCountryActivated ( int iCntryListItem ) +void CServerDlg::OnCustomDirectoryEditingFinished() { // apply new setting to the server and update it - pServer->SetServerCountry ( static_cast ( cbxLocationCountry->itemData ( iCntryListItem ).toInt() ) ); + pServer->SetDirectoryAddress ( edtCustomDirectory->text() ); - pServer->UpdateServerList(); + // update GUI dependencies + UpdateGUIDependencies(); } -void CServerDlg::OnDirectoryTypeActivated ( int iTypeIdx ) +void CServerDlg::OnLocationCountryCurrentIndexChanged ( int iCntryListItem ) { + // apply new setting to the server and update it + pServer->SetServerCountry ( static_cast ( cbxLocationCountry->itemData ( iCntryListItem ).toInt() ) ); +} +void CServerDlg::OnDirectoryTypeCurrentIndexChanged ( int iTypeIdx ) +{ // apply new setting to the server and update it - pServer->SetDirectoryType ( static_cast ( iTypeIdx ) ); - pServer->UpdateServerList(); + pServer->SetDirectoryType ( static_cast ( iTypeIdx - 1 ) ); // update GUI dependencies UpdateGUIDependencies(); @@ -604,6 +598,35 @@ void CServerDlg::OnClearRecordingDirClicked() } } +void CServerDlg::OnServerListPersistenceClicked() +{ + // get the current value from pServer + QString currentValue = pServer->GetServerListFileName(); + QFileDialog fileDialog; + fileDialog.setAcceptMode ( QFileDialog::AcceptSave ); + fileDialog.setOptions ( QFileDialog::DontUseNativeDialog | QFileDialog::HideNameFilterDetails ); + fileDialog.selectFile ( currentValue ); + if ( fileDialog.exec() && fileDialog.selectedFiles().size() == 1 ) + { + QString newFileName = fileDialog.selectedFiles().takeFirst(); + + if ( newFileName != currentValue ) + { + pServer->SetServerListFileName ( newFileName ); + UpdateGUIDependencies(); + } + } +} + +void CServerDlg::OnClearServerListPersistenceClicked() +{ + if ( pServer->GetServerListFileName() != "" ) + { + pServer->SetServerListFileName ( "" ); + UpdateGUIDependencies(); + } +} + void CServerDlg::OnSysTrayActivated ( QSystemTrayIcon::ActivationReason ActReason ) { #ifdef _WIN32 @@ -681,36 +704,52 @@ void CServerDlg::OnTimer() void CServerDlg::UpdateGUIDependencies() { - // get the states which define the GUI dependencies from the server - const bool bIsRegistered = pServer->GetServerRegistered(); - const ESvrRegStatus eSvrRegStatus = pServer->GetSvrRegStatus(); + const EDirectoryType directoryType = pServer->GetDirectoryType(); + cbxDirectoryType->setCurrentIndex ( static_cast ( directoryType ) + 1 ); - // if register server is not enabled, we disable all the configuration - // controls for the server list - cbxDirectoryType->setEnabled ( bIsRegistered ); - grbServerInfo->setEnabled ( bIsRegistered ); + const ESvrRegStatus eSvrRegStatus = pServer->GetSvrRegStatus(); + QString strStatus = svrRegStatusToString ( eSvrRegStatus ); + QString strFontColour = "darkGreen"; - QString strStatus = svrRegStatusToString ( eSvrRegStatus ); + if ( pServer->IsDirectoryServer() ) + { + strStatus = tr ( "Now a directory server" ); + } + else + { + switch ( eSvrRegStatus ) + { + case SRS_BAD_ADDRESS: + case SRS_TIME_OUT: + case SRS_SERVER_LIST_FULL: + case SRS_VERSION_TOO_OLD: + case SRS_NOT_FULFILL_REQUIREMENTS: + strFontColour = "red"; + break; + + default: + break; + } + } - switch ( eSvrRegStatus ) + if ( eSvrRegStatus == SRS_NOT_REGISTERED ) + { + lblRegSvrStatus->setText ( strStatus ); + } + else { - case SRS_BAD_ADDRESS: - case SRS_TIME_OUT: - case SRS_SERVER_LIST_FULL: - case SRS_VERSION_TOO_OLD: - case SRS_NOT_FULFILL_REQUIREMENTS: - strStatus = "" + strStatus + ""; - break; + lblRegSvrStatus->setText ( "" + strStatus + "" ); + } + + edtServerName->setText ( pServer->GetServerName() ); + edtLocationCity->setText ( pServer->GetServerCity() ); + cbxLocationCountry->setCurrentIndex ( cbxLocationCountry->findData ( static_cast ( pServer->GetServerCountry() ) ) ); - case SRS_REGISTERED: - strStatus = "" + strStatus + ""; - break; + tedWelcomeMessage->setText ( pServer->GetWelcomeMessage() ); - default: - break; - } + edtCustomDirectory->setText ( pServer->GetDirectoryAddress() ); - lblRegSvrStatus->setText ( strStatus ); + edtServerListPersistence->setText ( pServer->GetServerListFileName() ); } void CServerDlg::UpdateSystemTrayIcon ( const bool bIsActive ) diff --git a/src/serverdlg.h b/src/serverdlg.h index 415476ef84..ee3d742945 100644 --- a/src/serverdlg.h +++ b/src/serverdlg.h @@ -94,31 +94,37 @@ class CServerDlg : public CBaseDlg, private Ui_CServerDlgBase QMenu* pSystemTrayIconMenu; public slots: - void OnRegisterServerStateChanged ( int value ); - void OnStartOnOSStartStateChanged ( int value ); + // From the GUI + void OnDirectoryTypeCurrentIndexChanged ( int iTypeIdx ); + void OnServerNameEditingFinished(); + void OnLocationCityEditingFinished(); + void OnLocationCountryCurrentIndexChanged ( int iCntryListItem ); void OnEnableRecorderStateChanged ( int value ) { pServer->SetEnableRecording ( Qt::CheckState::Checked == value ); } + void OnNewRecordingClicked() { pServer->RequestNewRecording(); } + void OnWelcomeMessageChanged() { pServer->SetWelcomeMessage ( tedWelcomeMessage->toPlainText() ); } + void OnLanguageChanged ( QString strLanguage ) { pSettings->strLanguage = strLanguage; } void OnCustomDirectoryEditingFinished(); - void OnServerNameTextChanged ( const QString& strNewName ); - void OnLocationCityTextChanged ( const QString& strNewCity ); - void OnLocationCountryActivated ( int iCntryListItem ); - void OnDirectoryTypeActivated ( int iTypeIdx ); - void OnTimer(); - void OnServerStarted(); - void OnServerStopped(); - void OnSvrRegStatusChanged() { UpdateGUIDependencies(); } - void OnStopRecorder(); + void OnRecordingDirClicked(); + void OnClearRecordingDirClicked(); + void OnServerListPersistenceClicked(); + void OnClearServerListPersistenceClicked(); + void OnStartOnOSStartStateChanged ( int value ); + void OnEnableDelayPanningStateChanged ( int value ) { pServer->SetEnableDelayPanning ( Qt::CheckState::Checked == value ); } + void OnSysTrayMenuOpen() { ShowWindowInForeground(); } void OnSysTrayMenuHide() { hide(); } void OnSysTrayMenuExit() { close(); } void OnSysTrayActivated ( QSystemTrayIcon::ActivationReason ActReason ); - void OnWelcomeMessageChanged() { pServer->SetWelcomeMessage ( tedWelcomeMessage->toPlainText() ); } - void OnLanguageChanged ( QString strLanguage ) { pSettings->strLanguage = strLanguage; } - void OnEnableDelayPanningStateChanged ( int value ) { pServer->SetEnableDelayPanning ( Qt::CheckState::Checked == value ); } - void OnNewRecordingClicked() { pServer->RequestNewRecording(); } - void OnRecordingDirClicked(); - void OnClearRecordingDirClicked(); - void OnRecordingSessionStarted ( QString sessionDir ) { UpdateRecorderStatus ( sessionDir ); } + // From the Server + void OnServerStarted(); + void OnServerStopped(); + void OnSvrRegStatusChanged() { UpdateGUIDependencies(); } + void OnRecordingSessionStarted ( QString sessionDir ) { UpdateRecorderStatus ( sessionDir ); } + void OnStopRecorder(); void OnCLVersionAndOSReceived ( CHostAddress, COSUtil::EOpSystemType, QString strVersion ); + + // Our timer + void OnTimer(); }; diff --git a/src/serverdlgbase.ui b/src/serverdlgbase.ui index f7e76084dd..08cc3994b4 100644 --- a/src/serverdlgbase.ui +++ b/src/serverdlgbase.ui @@ -67,19 +67,12 @@ Server Setup - - - - Make My Server Public (Register My Server in the Server List) - - - - + - List + Directory @@ -254,7 +247,7 @@ - Custom Directory Server Address: + Custom Directory address @@ -263,6 +256,27 @@ + + + + + + Server List Filename + + + + + + + true + + + + + + + + @@ -313,7 +327,6 @@ lvwClients tabWidget - chbRegisterServer cbxDirectoryType edtServerName edtLocationCity @@ -327,7 +340,11 @@ edtRecordingDir tbtClearRecordingDir edtCustomDirectory + pbtServerListPersistence + edtServerListPersistence + tbtClearServerListPersistence chbStartOnOSStart + chbEnableDelayPanning diff --git a/src/serverlist.cpp b/src/serverlist.cpp index 6fefc39a40..3f93bc6943 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -101,15 +101,16 @@ CServerListManager::CServerListManager ( const quint16 iNPortNum, const int iNumChannels, const bool bNEnableIPv6, CProtocol* pNConLProt ) : - eDirectoryType ( AT_CUSTOM ), // must be AT_CUSTOM for the "no GUI" case + DirectoryType ( AT_NONE ), bEnableIPv6 ( bNEnableIPv6 ), - eSvrRegStatus ( SRS_UNREGISTERED ), + ServerListFileName ( strServerListFileName ), + strDirectoryAddress ( "" ), + bIsDirectoryServer ( false ), + eSvrRegStatus ( SRS_NOT_REGISTERED ), strMinServerVersion ( "" ), // disable version check with empty version pConnLessProtocol ( pNConLProt ), iSvrRegRetries ( 0 ) { - // set the directory server address (also bIsDirectoryServer) - SetDirectoryAddress ( sNDirectoryAddress ); // set the server internal address, including internal port number QHostAddress qhaServerPublicIP; @@ -183,48 +184,36 @@ CServerListManager::CServerListManager ( const quint16 iNPortNum, // per definition, the first entry in the server list is the own server ServerList.append ( ThisServerListEntry ); - // Clear the persistent serverlist file name - ServerListFileName.clear(); + // set the directory address - not the type, that gets done by app start up + SetDirectoryAddress ( sNDirectoryAddress ); - if ( bIsDirectoryServer ) + // whitelist parsing - only used when bIsDirectoryServer + if ( !strServerListFilter.isEmpty() ) { - // Load any persistent server list (create it if it is not there) - if ( !strServerListFileName.isEmpty() && ServerListFileName.isEmpty() ) - { - Load ( strServerListFileName ); - } + // split the different parameter strings + QStringList slWhitelistAddresses = strServerListFilter.split ( ";" ); + QHostAddress CurWhiteListAddress; - // whitelist parsing - if ( !strServerListFilter.isEmpty() ) + for ( int iIdx = 0; iIdx < slWhitelistAddresses.size(); iIdx++ ) { - // split the different parameter strings - QStringList slWhitelistAddresses = strServerListFilter.split ( ";" ); - QHostAddress CurWhiteListAddress; - - for ( int iIdx = 0; iIdx < slWhitelistAddresses.size(); iIdx++ ) + // check for special case: [version] + if ( ( slWhitelistAddresses.at ( iIdx ).length() > 2 ) && ( slWhitelistAddresses.at ( iIdx ).left ( 1 ) == "[" ) && + ( slWhitelistAddresses.at ( iIdx ).right ( 1 ) == "]" ) ) { - // check for special case: [version] - if ( ( slWhitelistAddresses.at ( iIdx ).length() > 2 ) && ( slWhitelistAddresses.at ( iIdx ).left ( 1 ) == "[" ) && - ( slWhitelistAddresses.at ( iIdx ).right ( 1 ) == "]" ) ) - { - strMinServerVersion = slWhitelistAddresses.at ( iIdx ).mid ( 1, slWhitelistAddresses.at ( iIdx ).length() - 2 ); - } - else if ( CurWhiteListAddress.setAddress ( slWhitelistAddresses.at ( iIdx ) ) ) - { - vWhiteList << CurWhiteListAddress; - qInfo() << qUtf8Printable ( QString ( "Whitelist entry added: %1" ).arg ( CurWhiteListAddress.toString() ) ); - } + strMinServerVersion = slWhitelistAddresses.at ( iIdx ).mid ( 1, slWhitelistAddresses.at ( iIdx ).length() - 2 ); + } + else if ( CurWhiteListAddress.setAddress ( slWhitelistAddresses.at ( iIdx ) ) ) + { + vWhiteList << CurWhiteListAddress; + qInfo() << qUtf8Printable ( QString ( "Whitelist entry added: %1" ).arg ( CurWhiteListAddress.toString() ) ); } } } - // start the one shot timer for determining if this is a + // prepare the one shot timer for determining if this is a // permanent registered server - if ( !bIsDirectoryServer ) - { - // 1 minute = 60 * 1000 ms - QTimer::singleShot ( SERVLIST_TIME_PERMSERV_MINUTES * 60000, this, SLOT ( OnTimerIsPermanent() ) ); - } + TimerIsPermanent.setSingleShot ( true ); + TimerIsPermanent.setInterval ( SERVLIST_TIME_PERMSERV_MINUTES * 60000 ); // prepare the register server response timer (single shot timer) TimerCLRegisterServerResp.setSingleShot ( true ); @@ -237,13 +226,46 @@ CServerListManager::CServerListManager ( const quint16 iNPortNum, QObject::connect ( &TimerPingServers, &QTimer::timeout, this, &CServerListManager::OnTimerPingServers ); - QObject::connect ( &TimerRegistering, &QTimer::timeout, this, &CServerListManager::OnTimerRefreshRegistration ); + QObject::connect ( &TimerRefreshRegistration, &QTimer::timeout, this, &CServerListManager::OnTimerRefreshRegistration ); QObject::connect ( &TimerCLRegisterServerResp, &QTimer::timeout, this, &CServerListManager::OnTimerCLRegisterServerResp ); + QObject::connect ( &TimerIsPermanent, &QTimer::timeout, this, &CServerListManager::OnTimerIsPermanent ); + QObject::connect ( QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &CServerListManager::OnAboutToQuit ); } +// set server infos -> per definition the server info of this server is +// stored in the first entry of the list, we assume here that the first +// entry is correctly created in the constructor of the class +// and, if registered, refresh the server list entry +void CServerListManager::SetServerName ( const QString& strNewName ) +{ + if ( ServerList[0].strName != strNewName ) + { + ServerList[0].strName = strNewName; + SetRegistered ( eSvrRegStatus != SRS_NOT_REGISTERED ); + } +} + +void CServerListManager::SetServerCity ( const QString& strNewCity ) +{ + if ( ServerList[0].strCity != strNewCity ) + { + ServerList[0].strCity = strNewCity; + SetRegistered ( eSvrRegStatus != SRS_NOT_REGISTERED ); + } +} + +void CServerListManager::SetServerCountry ( const QLocale::Country eNewCountry ) +{ + if ( ServerList[0].eCountry != eNewCountry ) + { + ServerList[0].eCountry = eNewCountry; + SetRegistered ( eSvrRegStatus != SRS_NOT_REGISTERED ); + } +} + void CServerListManager::SetDirectoryAddress ( const QString sNDirectoryAddress ) { // if the address has not actually changed, do nothing @@ -252,110 +274,165 @@ void CServerListManager::SetDirectoryAddress ( const QString sNDirectoryAddress return; } - // if we are registered to a custom directory server, unregister before updating the name - if ( eDirectoryType == AT_CUSTOM && GetSvrRegStatus() == SRS_REGISTERED ) + if ( DirectoryType != AT_CUSTOM ) { - Unregister(); + // just save the new name + strDirectoryAddress = sNDirectoryAddress; + return; } + // sets the lock + Unregister(); + QMutexLocker locker ( &Mutex ); // now save the new name strDirectoryAddress = sNDirectoryAddress; SetIsDirectoryServer(); + + locker.unlock(); + + // sets the lock + Register(); } void CServerListManager::SetDirectoryType ( const EDirectoryType eNCSAT ) { - // if the type is changing, unregister before updating - if ( eNCSAT != eDirectoryType && GetSvrRegStatus() == SRS_REGISTERED ) + // if the directory type is not changing, do nothing + if ( eNCSAT == DirectoryType ) { - Unregister(); + return; } + // sets the lock + Unregister(); + QMutexLocker locker ( &Mutex ); // now update the server type - eDirectoryType = eNCSAT; + DirectoryType = eNCSAT; SetIsDirectoryServer(); + + locker.unlock(); + + // sets the lock + Register(); } void CServerListManager::SetIsDirectoryServer() { - // per definition: If we are in server mode and the directory server address - // is the localhost address, and set to Custom, we are in directory server mode. - bool bNIsDirectoryServer = - ( ( !strDirectoryAddress.compare ( "localhost", Qt::CaseInsensitive ) || !strDirectoryAddress.compare ( "127.0.0.1" ) ) && - ( eDirectoryType == AT_CUSTOM ) ); - if ( bIsDirectoryServer != bNIsDirectoryServer ) + // this is called with the lock set + + // per definition: If we are registered and the directory + // is the localhost address, we are in directory server mode. + bool bNIsDirectoryServer = DirectoryType == AT_CUSTOM && + ( !strDirectoryAddress.compare ( "localhost", Qt::CaseInsensitive ) || !strDirectoryAddress.compare ( "127.0.0.1" ) ); + + if ( bIsDirectoryServer == bNIsDirectoryServer ) { - bIsDirectoryServer = bNIsDirectoryServer; - qInfo() << ( bIsDirectoryServer ? "Now a directory server" : "No longer a directory server" ); + return; + } + + bIsDirectoryServer = bNIsDirectoryServer; + + if ( bIsDirectoryServer ) + { + qInfo() << "Now a directory server"; + // Load any persistent server list (create it if it is not there) + (void) Load(); + } + else + { + qInfo() << "No longer a directory server"; } } -void CServerListManager::Update() +// When we unregister, set the status and stop timers +void CServerListManager::Unregister() { + // if not currently registered, nothing needs doing + if ( DirectoryType == AT_NONE || ( DirectoryType == AT_CUSTOM && strDirectoryAddress.isEmpty() ) || eSvrRegStatus == SRS_NOT_REGISTERED ) + { + return; + } + + // Update server registration status - sets the lock + SetRegistered ( false ); + + // this is called without the lock set QMutexLocker locker ( &Mutex ); - if ( bEnabled ) + // disable service -> stop timer + if ( bIsDirectoryServer ) { - if ( bIsDirectoryServer ) - { - // start timer for polling the server list if enabled - // 1 minute = 60 * 1000 ms - TimerPollList.start ( SERVLIST_POLL_TIME_MINUTES * 60000 ); + TimerPollList.stop(); + TimerPingServerInList.stop(); + } + else + { + TimerCLRegisterServerResp.stop(); + TimerRefreshRegistration.stop(); + TimerPingServers.stop(); + TimerIsPermanent.stop(); + } + ServerList[0].bPermanentOnline = false; +} - // start timer for sending ping messages to servers in the list - TimerPingServerInList.start ( SERVLIST_UPDATE_PING_SERVERS_MS ); - } - else - { - // initiate registration right away so that we do not have to wait - // for the first time out of the timer to get registered. - // note that we have to unlock the mutex before calling the function - // since inside this function the mutex is locked, too - locker.unlock(); - { - OnTimerRefreshRegistration(); - } - locker.relock(); +// When we register, set the status and start timers +void CServerListManager::Register() +{ + // if cannot currently register, or we have registered, nothing needs doing + if ( DirectoryType == AT_NONE || ( DirectoryType == AT_CUSTOM && strDirectoryAddress.isEmpty() ) || eSvrRegStatus == SRS_REGISTERED ) + { + // there are edge cases during registration... + // maybe ! ( SRS_NOT_REGISTERED || SRS_BAD_ADDRESS ) + return; + } - // reset the retry counter to zero because update was called - iSvrRegRetries = 0; + // Update server registration status - sets the lock + SetRegistered ( true ); - // start timer for registration timeout - TimerCLRegisterServerResp.start(); + // this is called without the lock set + QMutexLocker locker ( &Mutex ); - // start timer for registering this server at the directory server - // 1 minute = 60 * 1000 ms - TimerRegistering.start ( SERVLIST_REGIST_INTERV_MINUTES * 60000 ); - - // Start timer for ping the directory server in short intervals to - // keep the port open at the NAT router. - // If no NAT is used, we send the messages anyway since they do - // not hurt (very low traffic). We also reuse the same update - // time as used in the directory server for pinging the registered - // servers. - TimerPingServers.start ( SERVLIST_UPDATE_PING_SERVERS_MS ); - } + if ( bIsDirectoryServer ) + { + // start timer for polling the server list if enabled + // 1 minute = 60 * 1000 ms + TimerPollList.start ( SERVLIST_POLL_TIME_MINUTES * 60000 ); + + // start timer for sending ping messages to servers in the list + TimerPingServerInList.start ( SERVLIST_UPDATE_PING_SERVERS_MS ); + + // directory is permanent + ServerList[0].bPermanentOnline = true; } else { - // disable service -> stop timer - if ( bIsDirectoryServer ) - { - TimerPollList.stop(); - TimerPingServerInList.stop(); - } - else - { - TimerCLRegisterServerResp.stop(); - TimerRegistering.stop(); - TimerPingServers.stop(); - } + // reset the retry counter to zero because update was called + iSvrRegRetries = 0; + + // start timer for registration timeout - this gets restarted + // in OnTimerCLRegisterServerResp on failure + TimerCLRegisterServerResp.start(); + + // start timer for registering this server at the directory server + // 1 minute = 60 * 1000 ms + TimerRefreshRegistration.start ( SERVLIST_REGIST_INTERV_MINUTES * 60000 ); + + // Start timer for ping the directory server in short intervals to + // keep the port open at the NAT router. + // If no NAT is used, we send the messages anyway since they do + // not hurt (very low traffic). We also reuse the same update + // time as used in the directory server for pinging the registered + // servers. + TimerPingServers.start ( SERVLIST_UPDATE_PING_SERVERS_MS ); + + // start the one shot timer for determining if this is a + // permanent registered server + TimerIsPermanent.start(); } } @@ -406,7 +483,7 @@ void CServerListManager::Append ( const CHostAddress& InetAddr, const CServerCoreInfo& ServerInfo, const QString strVersion ) { - if ( bIsDirectoryServer && bEnabled ) + if ( bIsDirectoryServer ) { qInfo() << qUtf8Printable ( QString ( "Requested to register entry for %1 (%2): %3" ) .arg ( InetAddr.toString() ) @@ -479,7 +556,7 @@ void CServerListManager::Append ( const CHostAddress& InetAddr, void CServerListManager::Remove ( const CHostAddress& InetAddr ) { - if ( bIsDirectoryServer && bEnabled ) + if ( bIsDirectoryServer ) { qInfo() << qUtf8Printable ( QString ( "Requested to unregister entry for %1" ).arg ( InetAddr.toString() ) ); @@ -500,7 +577,7 @@ void CServerListManager::RetrieveAll ( const CHostAddress& InetAddr ) { QMutexLocker locker ( &Mutex ); - if ( bIsDirectoryServer && bEnabled ) + if ( bIsDirectoryServer ) { const int iCurServerListSize = ServerList.size(); @@ -562,6 +639,8 @@ void CServerListManager::RetrieveAll ( const CHostAddress& InetAddr ) int CServerListManager::IndexOf ( CHostAddress haSearchTerm ) { + // Called with lock set. + // Find the server in the list. The very first list entry // per definition is the directory server // (i.e., this server). @@ -575,22 +654,53 @@ int CServerListManager::IndexOf ( CHostAddress haSearchTerm ) return INVALID_INDEX; } -void CServerListManager::Load ( const QString strServerList ) +bool CServerListManager::SetServerListFileName ( QString strFilename ) { - QFile file ( strServerList ); + QMutexLocker locker ( &Mutex ); + + if ( ServerListFileName == strFilename ) + { + return true; + } + + if ( !ServerListFileName.isEmpty() ) + { + // Save once to the old filename + Save(); + } + + ServerListFileName = strFilename; + return Load(); +} + +bool CServerListManager::Load() +{ + // this is called with the lock set + + if ( !bIsDirectoryServer || ServerListFileName.isEmpty() ) + { + // this gets called again if either of the above change + return true; + } + + QFile file ( ServerListFileName ); if ( !file.open ( QIODevice::ReadWrite | QIODevice::Text ) ) { qWarning() << qUtf8Printable ( QString ( "Could not open '%1' for read/write. Please check that Jamulus has permission (and that there is free space)." ) .arg ( ServerListFileName ) ); - return; + ServerListFileName.clear(); + return false; } + qInfo() << "Loading persistent server list file:" << ServerListFileName; + + // do not lose our entry + CServerListEntry serverListEntry = ServerList[0]; + ServerList.clear(); + ServerList.append ( serverListEntry ); // use entire file content for the persistent server list - // and remember it for later writing - qInfo() << "Setting persistent server list file:" << strServerList; - ServerListFileName = strServerList; CHostAddress haServerHostAddr; QTextStream in ( &file ); @@ -612,7 +722,7 @@ void CServerListManager::Load ( const QString strServerList ) continue; } - CServerListEntry serverListEntry = + serverListEntry = CServerListEntry::parse ( slLine[0], slLine[1], slLine[2], slLine[3], slLine[4], slLine[5], slLine[6].toInt() != 0, bEnableIPv6 ); // We expect servers to have addresses... @@ -628,10 +738,14 @@ void CServerListManager::Load ( const QString strServerList ) .arg ( serverListEntry.strName ) ); ServerList.append ( serverListEntry ); } + + return true; } void CServerListManager::Save() { + // this is called with the lock set + if ( ServerListFileName.isEmpty() ) { return; @@ -699,7 +813,7 @@ void CServerListManager::OnTimerPingServers() { QMutexLocker locker ( &Mutex ); - // first check if directory server address is valid + // first check if directory address is valid if ( !( DirectoryAddress == CHostAddress() ) ) { // send empty message to directory server to keep NAT port open -> we do @@ -740,42 +854,66 @@ void CServerListManager::SetRegistered ( const bool bIsRegister ) // any time QMutexLocker locker ( &Mutex ); - // get the correct directory server address - const QString strCurrentDirectoryAddress = NetworkUtil::GetDirectoryAddress ( eDirectoryType, strDirectoryAddress ); + if ( !bIsRegister && eSvrRegStatus == SRS_NOT_REGISTERED ) + { + // not wanting to set registered, not registered, nothing to do + return; + } - // For a registered server, the server properties are stored in the - // very first item in the server list (which is actually no server list - // but just one item long for the registered server). + if ( bIsDirectoryServer ) + { + // this IS the directory, no network message to worry about + SetSvrRegStatus ( bIsRegister ? SRS_REGISTERED : SRS_NOT_REGISTERED ); + return; + } + + // get the correct directory address // Note that we always have to parse the server address again since if // it is an URL of a dynamic IP address, the IP address might have // changed in the meanwhile. + // Allow IPv4 only for communicating with Directories + const QString strNetworkAddress = NetworkUtil::GetDirectoryAddress ( DirectoryType, strDirectoryAddress ); + const bool bDirectoryAddressValid = NetworkUtil().ParseNetworkAddress ( strNetworkAddress, DirectoryAddress, false ); - // Allow IPv4 only for communicating with Directory Servers - if ( NetworkUtil().ParseNetworkAddress ( strCurrentDirectoryAddress, DirectoryAddress, false ) ) + if ( bIsRegister ) { - if ( bIsRegister ) + if ( bDirectoryAddressValid ) { // register server SetSvrRegStatus ( SRS_REQUESTED ); + // For a registered server, the server properties are stored in the + // very first item in the server list (which is actually no server list + // but just one item long for the registered server). pConnLessProtocol->CreateCLRegisterServerExMes ( DirectoryAddress, ServerPublicIP, ServerList[0] ); } else { - // unregister server - SetSvrRegStatus ( SRS_UNREGISTERED ); - - pConnLessProtocol->CreateCLUnregisterServerMes ( DirectoryAddress ); + SetSvrRegStatus ( SRS_BAD_ADDRESS ); } } else { - SetSvrRegStatus ( SRS_BAD_ADDRESS ); + // unregister server if it is registered - regardless of success of parse + SetSvrRegStatus ( SRS_NOT_REGISTERED ); + + if ( bDirectoryAddressValid ) + { + pConnLessProtocol->CreateCLUnregisterServerMes ( DirectoryAddress ); + } } } void CServerListManager::SetSvrRegStatus ( ESvrRegStatus eNSvrRegStatus ) { + // this is called with the lock set + + // if status isn't a change, do nothing + if ( eNSvrRegStatus == eSvrRegStatus ) + { + return; + } + // output regirstation result/update on the console qInfo() << qUtf8Printable ( QString ( "Server Registration Status update: %1" ).arg ( svrRegStatusToString ( eNSvrRegStatus ) ) ); diff --git a/src/serverlist.h b/src/serverlist.h index f99cbb3002..075d3ddc17 100644 --- a/src/serverlist.h +++ b/src/serverlist.h @@ -143,28 +143,20 @@ class CServerListManager : public QObject const bool bNEnableIPv6, CProtocol* pNConLProt ); - void SetEnabled ( const bool bState ) { bEnabled = bState; } - bool GetEnabled() const { return bEnabled; } - - void Unregister() { SetRegistered ( false ); } - - // set server infos -> per definition the server info of this server is - // stored in the first entry of the list, we assume here that the first - // entry is correctly created in the constructor of the class - void SetServerName ( const QString& strNewName ) { ServerList[0].strName = strNewName; } + void SetServerName ( const QString& strNewName ); QString GetServerName() { return ServerList[0].strName; } - void SetServerCity ( const QString& strNewCity ) { ServerList[0].strCity = strNewCity; } + void SetServerCity ( const QString& strNewCity ); QString GetServerCity() { return ServerList[0].strCity; } - void SetServerCountry ( const QLocale::Country eNewCountry ) { ServerList[0].eCountry = eNewCountry; } + void SetServerCountry ( const QLocale::Country eNewCountry ); QLocale::Country GetServerCountry() { return ServerList[0].eCountry; } void SetDirectoryAddress ( const QString sNDirectoryAddress ); QString GetDirectoryAddress() { return strDirectoryAddress; } void SetDirectoryType ( const EDirectoryType eNCSAT ); - EDirectoryType GetDirectoryType() { return eDirectoryType; } + EDirectoryType GetDirectoryType() { return DirectoryType; } bool IsDirectoryServer() const { return bIsDirectoryServer; } @@ -180,34 +172,40 @@ class CServerListManager : public QObject void StoreRegistrationResult ( ESvrRegResult eStatus ); + QString GetServerListFileName() { return ServerListFileName; } + bool SetServerListFileName ( QString strFilename ); + protected: void SetIsDirectoryServer(); + void Unregister(); + void Register(); + void SetRegistered ( bool bIsRegister ); + int IndexOf ( CHostAddress haSearchTerm ); - void Load ( const QString strServerList ); + bool Load(); void Save(); - void SetRegistered ( const bool bIsRegister ); void SetSvrRegStatus ( ESvrRegStatus eNSvrRegStatus ); QMutex Mutex; - bool bEnabled; - - QList ServerList; + CHostAddress DirectoryAddress; + EDirectoryType DirectoryType; - QString strDirectoryAddress; - EDirectoryType eDirectoryType; - bool bIsDirectoryServer; - bool bEnableIPv6; + bool bEnableIPv6; - // server registration status - ESvrRegStatus eSvrRegStatus; - - CHostAddress DirectoryAddress; CHostAddress ServerPublicIP; CHostAddress ServerPublicIP6; QString ServerListFileName; + QList ServerList; + + QString strDirectoryAddress; + bool bIsDirectoryServer; + + // server registration status + ESvrRegStatus eSvrRegStatus; + QList vWhiteList; QString strMinServerVersion; @@ -219,8 +217,9 @@ class CServerListManager : public QObject QTimer TimerPollList; QTimer TimerPingServerInList; QTimer TimerPingServers; - QTimer TimerRegistering; + QTimer TimerRefreshRegistration; QTimer TimerCLRegisterServerResp; + QTimer TimerIsPermanent; public slots: void OnTimerPollList(); @@ -228,9 +227,13 @@ public slots: void OnTimerPingServers(); void OnTimerRefreshRegistration() { SetRegistered ( true ); } void OnTimerCLRegisterServerResp(); - void OnTimerIsPermanent() { ServerList[0].bPermanentOnline = true; } - void OnAboutToQuit() { Save(); } + + void OnAboutToQuit() + { + QMutexLocker locker ( &Mutex ); + Save(); + } signals: void SvrRegStatusChanged(); diff --git a/src/settings.cpp b/src/settings.cpp index eb8884a9f5..5136505217 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -761,6 +761,7 @@ void CClientSettings::WriteFaderSettingsToXML ( QDomDocument& IniXMLDocument ) } // Server settings ------------------------------------------------------------- +// that this gets called means we are not headless void CServerSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, const QList& CommandLineOptions ) { int iValue; @@ -769,55 +770,6 @@ void CServerSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, // window position of the main window vecWindowPosMain = FromBase64ToByteArray ( GetIniSetting ( IniXMLDocument, "server", "winposmain_base64" ) ); - // directory type (note that it is important - // to set this setting prior to the "directory server address") - // clang-format off -// TODO compatibility to old version -if ( GetNumericIniSet ( IniXMLDocument, "server", "centservaddrtype", 0, static_cast ( AT_CUSTOM ), iValue ) ) -{ - pServer->SetDirectoryType ( static_cast ( iValue ) ); -} - // clang-format on - else if ( GetNumericIniSet ( IniXMLDocument, "server", "directorytype", 0, static_cast ( AT_CUSTOM ), iValue ) ) - { - pServer->SetDirectoryType ( static_cast ( iValue ) ); - } - else - { - // if no address type is given, use the default directory server - pServer->SetDirectoryType ( AT_DEFAULT ); - } - - // clang-format off -// TODO compatibility to old version -if ( GetFlagIniSet ( IniXMLDocument, "server", "defcentservaddr", bValue ) ) -{ - // only the case that manual was set in old ini must be considered - if ( !bValue ) - { - pServer->SetDirectoryType ( AT_CUSTOM ); - } -} - // clang-format on - - if ( !CommandLineOptions.contains ( "--centralserver" ) && !CommandLineOptions.contains ( "--directoryserver" ) ) - { - // directory server address (to be set after the "use default directory - // server" address) - // clang-format off -// TODO compatibility to old version -QString directoryAddress = GetIniSetting ( IniXMLDocument, "server", "centralservaddr", "" ); - // clang-format on - directoryAddress = GetIniSetting ( IniXMLDocument, "server", "directoryaddress", directoryAddress ); - pServer->SetDirectoryAddress ( directoryAddress ); - } - - // server list enabled flag - if ( GetFlagIniSet ( IniXMLDocument, "server", "servlistenabled", bValue ) ) - { - pServer->SetServerRegistered ( bValue ); - } - // name/city/country if ( !CommandLineOptions.contains ( "--serverinfo" ) ) { @@ -859,6 +811,82 @@ QString directoryAddress = GetIniSetting ( IniXMLDocument, "server", "centralser pServer->SetRecordingDir ( FromBase64ToString ( GetIniSetting ( IniXMLDocument, "server", "recordingdir_base64" ) ) ); } + // to avoid multiple registrations, must do this after collecting serverinfo + if ( !CommandLineOptions.contains ( "--centralserver" ) && !CommandLineOptions.contains ( "--directoryserver" ) ) + { + // custom directory + // CServerListManager defaults to command line argument (or "" if not passed) + // Server GUI defaults to "" + QString directoryAddress = ""; + // clang-format off +// TODO compatibility to old version < 3.8.2 +directoryAddress = GetIniSetting ( IniXMLDocument, "server", "centralservaddr", directoryAddress ); + // clang-format on + directoryAddress = GetIniSetting ( IniXMLDocument, "server", "directoryaddress", directoryAddress ); + + pServer->SetDirectoryAddress ( directoryAddress ); + } + + // directory type + // CServerListManager defaults to AT_NONE + // Server GUI defaults to AT_DEFAULT + // Because type could be AT_CUSTOM, it has to be set after the address to avoid multiple registrations + EDirectoryType directoryType = AT_DEFAULT; + + // if command line is set, use it + if ( CommandLineOptions.contains ( "--centralserver" ) || CommandLineOptions.contains ( "--directoryserver" ) ) + { + directoryType = AT_CUSTOM; + } + else + { + // clang-format off +// TODO compatibility to old version < 3.8.2 +// if "servlistenabled" exists and is false, set directory type to AT_NONE +if ( GetFlagIniSet ( IniXMLDocument, "server", "servlistenabled", bValue ) && !bValue ) +{ + directoryType = AT_NONE; +} + // clang-format on + + // clang-format off +// TODO compatibility to old version < ????? +// only the case that manual was set in old ini must be considered +if ( GetFlagIniSet ( IniXMLDocument, "server", "defcentservaddr", bValue ) && !bValue ) +{ + directoryType = AT_CUSTOM; +} +else + // clang-format on + + // if "directorytype" itself is set, use it (note "AT_NONE", "AT_DEFAULT" and "AT_CUSTOM" are min/max directory type here) + // clang-format off +// TODO compatibility to old version < 3.8.2 +if ( GetNumericIniSet ( IniXMLDocument, "server", "centservaddrtype", static_cast ( AT_DEFAULT ), static_cast ( AT_CUSTOM ), iValue ) ) +{ + directoryType = static_cast ( iValue ); +} +else + // clang-format on + if ( GetNumericIniSet ( IniXMLDocument, + "server", + "directorytype", + static_cast ( AT_NONE ), + static_cast ( AT_CUSTOM ), + iValue ) ) + { + directoryType = static_cast ( iValue ); + } + } + + pServer->SetDirectoryType ( directoryType ); + + // server list persistence file name + if ( !CommandLineOptions.contains ( "--directoryfile" ) ) + { + pServer->SetServerListFileName ( FromBase64ToString ( GetIniSetting ( IniXMLDocument, "server", "directoryfile_base64" ) ) ); + } + // start minimized on OS start if ( !CommandLineOptions.contains ( "--startminimized" ) ) { @@ -883,15 +911,9 @@ void CServerSettings::WriteSettingsToXML ( QDomDocument& IniXMLDocument ) // window position of the main window PutIniSetting ( IniXMLDocument, "server", "winposmain_base64", ToBase64 ( vecWindowPosMain ) ); - // directory server address - PutIniSetting ( IniXMLDocument, "server", "directoryaddress", pServer->GetDirectoryAddress() ); - // directory type SetNumericIniSet ( IniXMLDocument, "server", "directorytype", static_cast ( pServer->GetDirectoryType() ) ); - // server list enabled flag - SetFlagIniSet ( IniXMLDocument, "server", "servlistenabled", pServer->GetServerRegistered() ); - // name PutIniSetting ( IniXMLDocument, "server", "name", pServer->GetServerName() ); @@ -913,9 +935,18 @@ void CServerSettings::WriteSettingsToXML ( QDomDocument& IniXMLDocument ) // base recording directory PutIniSetting ( IniXMLDocument, "server", "recordingdir_base64", ToBase64 ( pServer->GetRecordingDir() ) ); + // custom directory + PutIniSetting ( IniXMLDocument, "server", "directoryaddress", pServer->GetDirectoryAddress() ); + + // server list persistence file name + PutIniSetting ( IniXMLDocument, "server", "directoryfile_base64", ToBase64 ( pServer->GetServerListFileName() ) ); + // start minimized on OS start SetFlagIniSet ( IniXMLDocument, "server", "autostartmin", pServer->GetAutoRunMinimized() ); // delay panning SetFlagIniSet ( IniXMLDocument, "server", "delaypan", pServer->IsDelayPanningEnabled() ); + + // we MUST do this after saving the value and Save() is only called OnAboutToQuit() + pServer->SetDirectoryType ( AT_NONE ); } diff --git a/src/util.h b/src/util.h index c504af7a1a..8de9c9e74b 100644 --- a/src/util.h +++ b/src/util.h @@ -558,6 +558,7 @@ enum EChSortType enum EDirectoryType { // used for settings -> enum values should be fixed + AT_NONE = -1, // means not registered, "invalid value" AT_DEFAULT = 0, AT_ANY_GENRE2 = 1, AT_ANY_GENRE3 = 2, @@ -572,8 +573,8 @@ inline QString DirectoryTypeToString ( EDirectoryType eAddrType ) { switch ( eAddrType ) { - case AT_CUSTOM: - return QCoreApplication::translate ( "CClientSettingsDlg", "Custom" ); + case AT_NONE: + return QCoreApplication::translate ( "CServerDlg", "None" ); case AT_ANY_GENRE2: return QCoreApplication::translate ( "CClientSettingsDlg", "Any Genre 2" ); @@ -593,6 +594,9 @@ inline QString DirectoryTypeToString ( EDirectoryType eAddrType ) case AT_GENRE_CHORAL: return QCoreApplication::translate ( "CClientSettingsDlg", "Genre Choral/Barbershop" ); + case AT_CUSTOM: + return QCoreApplication::translate ( "CClientSettingsDlg", "Custom" ); + default: // AT_DEFAULT return QCoreApplication::translate ( "CClientSettingsDlg", "Any Genre 1" ); } @@ -601,7 +605,7 @@ inline QString DirectoryTypeToString ( EDirectoryType eAddrType ) // Server registration state --------------------------------------------- enum ESvrRegStatus { - SRS_UNREGISTERED, + SRS_NOT_REGISTERED, SRS_BAD_ADDRESS, SRS_REQUESTED, SRS_TIME_OUT, @@ -616,8 +620,8 @@ inline QString svrRegStatusToString ( ESvrRegStatus eSvrRegStatus ) { switch ( eSvrRegStatus ) { - case SRS_UNREGISTERED: - return QCoreApplication::translate ( "CServerDlg", "Unregistered" ); + case SRS_NOT_REGISTERED: + return QCoreApplication::translate ( "CServerDlg", "Not registered" ); case SRS_BAD_ADDRESS: return QCoreApplication::translate ( "CServerDlg", "Bad address" );