1 /******************************************************************************\
2  * Copyright (c) 2004-2020
3  *
4  * Author(s):
5  *  Volker Fischer
6  *
7  ******************************************************************************
8  *
9  * This program is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free Software
11  * Foundation; either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
22  *
23 \******************************************************************************/
24 
25 #include "serverlist.h"
26 
27 /* Implementation *************************************************************/
28 
29 // --- CServerListEntry ---
parse(QString strHAddr,QString strLHAddr,QString sName,QString sCity,QString strCountry,QString strNumClients,bool isPermanent,bool bEnableIPv6)30 CServerListEntry CServerListEntry::parse ( QString strHAddr,
31                                            QString strLHAddr,
32                                            QString sName,
33                                            QString sCity,
34                                            QString strCountry,
35                                            QString strNumClients,
36                                            bool    isPermanent,
37                                            bool    bEnableIPv6 )
38 {
39     CHostAddress haServerHostAddr;
40     NetworkUtil::ParseNetworkAddress ( strHAddr, haServerHostAddr, bEnableIPv6 );
41     if ( CHostAddress() == haServerHostAddr )
42     {
43         // do not proceed without server host address!
44         return CServerListEntry();
45     }
46 
47     CHostAddress haServerLocalAddr;
48     NetworkUtil::ParseNetworkAddress ( strLHAddr, haServerLocalAddr, bEnableIPv6 );
49     if ( haServerLocalAddr.iPort == 0 )
50     {
51         haServerLocalAddr.iPort = haServerHostAddr.iPort;
52     }
53 
54     // Capture parsing success of integers
55     bool ok;
56 
57     QLocale::Country lcCountry = QLocale::AnyCountry;
58     int              iCountry  = strCountry.trimmed().toInt ( &ok );
59     if ( ok && iCountry >= 0 && iCountry <= QLocale::LastCountry )
60     {
61         lcCountry = static_cast<QLocale::Country> ( iCountry );
62     }
63 
64     int iNumClients = strNumClients.trimmed().toInt ( &ok );
65     if ( !ok )
66     {
67         iNumClients = 10;
68     }
69 
70     return CServerListEntry ( haServerHostAddr,
71                               haServerLocalAddr,
72                               CServerCoreInfo ( FromBase64ToString ( sName.trimmed().left ( MAX_LEN_SERVER_NAME ) ),
73                                                 lcCountry,
74                                                 FromBase64ToString ( sCity.trimmed().left ( MAX_LEN_SERVER_CITY ) ),
75                                                 iNumClients,
76                                                 isPermanent ) );
77 }
78 
toCSV()79 QString CServerListEntry::toCSV()
80 {
81     QStringList sl;
82 
83     sl.append ( this->HostAddr.toString() );
84     sl.append ( this->LHostAddr.toString() );
85     sl.append ( ToBase64 ( this->strName ) );
86     sl.append ( ToBase64 ( this->strCity ) );
87     sl.append ( QString::number ( this->eCountry ) );
88     sl.append ( QString::number ( this->iMaxNumClients ) );
89     sl.append ( QString::number ( this->bPermanentOnline ) );
90 
91     return sl.join ( ";" );
92 }
93 
94 // --- CServerListManager ---
CServerListManager(const quint16 iNPortNum,const QString & sNCentServAddr,const QString & strServerListFileName,const QString & strServerInfo,const QString & strServerListFilter,const QString & strServerPublicIP,const int iNumChannels,const bool bNEnableIPv6,CProtocol * pNConLProt)95 CServerListManager::CServerListManager ( const quint16  iNPortNum,
96                                          const QString& sNCentServAddr,
97                                          const QString& strServerListFileName,
98                                          const QString& strServerInfo,
99                                          const QString& strServerListFilter,
100                                          const QString& strServerPublicIP,
101                                          const int      iNumChannels,
102                                          const bool     bNEnableIPv6,
103                                          CProtocol*     pNConLProt ) :
104     eCentralServerAddressType ( AT_CUSTOM ), // must be AT_CUSTOM for the "no GUI" case
105     bEnableIPv6 ( bNEnableIPv6 ),
106     eSvrRegStatus ( SRS_UNREGISTERED ),
107     strMinServerVersion ( "" ), // disable version check with empty version
108     pConnLessProtocol ( pNConLProt ),
109     iSvrRegRetries ( 0 )
110 {
111     // set the directory server address (also bIsCentralServer)
112     SetCentralServerAddress ( sNCentServAddr );
113 
114     // set the server internal address, including internal port number
115     QHostAddress qhaServerPublicIP;
116 
117     if ( strServerPublicIP == "" )
118     {
119         // No user-supplied override via --serverpublicip -> use auto-detection
120         qhaServerPublicIP = NetworkUtil::GetLocalAddress().InetAddr;
121     }
122     else
123     {
124         // User-supplied --serverpublicip
125         qhaServerPublicIP = QHostAddress ( strServerPublicIP );
126     }
127     qDebug() << "Using" << qhaServerPublicIP.toString() << "as external IP.";
128     SlaveCurLocalHostAddress = CHostAddress ( qhaServerPublicIP, iNPortNum );
129 
130     if ( bEnableIPv6 )
131     {
132         // set the server internal address, including internal port number
133         QHostAddress qhaServerPublicIP6;
134 
135         qhaServerPublicIP6 = NetworkUtil::GetLocalAddress6().InetAddr;
136         qDebug() << "Using" << qhaServerPublicIP6.toString() << "as external IPv6.";
137         SlaveCurLocalHostAddress6 = CHostAddress ( qhaServerPublicIP6, iNPortNum );
138     }
139 
140     // prepare the server info information
141     QStringList slServInfoSeparateParams;
142     int         iServInfoNumSplitItems = 0;
143 
144     if ( !strServerInfo.isEmpty() )
145     {
146         // split the different parameter strings
147         slServInfoSeparateParams = strServerInfo.split ( ";" );
148 
149         // get the number of items in the split list
150         iServInfoNumSplitItems = slServInfoSeparateParams.count();
151     }
152 
153     // Init server list entry (server info for this server) with defaults. Per
154     // definition the client substitutes the IP address of the directory server
155     // itself for his server list. If we are a directory server, we assume that
156     // we are a permanent server.
157     CServerListEntry
158         ThisServerListEntry ( CHostAddress(), SlaveCurLocalHostAddress, "", QLocale::system().country(), "", iNumChannels, bIsCentralServer );
159 
160     // parse the server info string according to definition:
161     // [this server name];[this server city];[this server country as QLocale ID] (; ... ignored)
162     // per definition, we expect at least three parameters
163     if ( iServInfoNumSplitItems >= 3 )
164     {
165         // [this server name]
166         ThisServerListEntry.strName = slServInfoSeparateParams[0].left ( MAX_LEN_SERVER_NAME );
167 
168         // [this server city]
169         ThisServerListEntry.strCity = slServInfoSeparateParams[1].left ( MAX_LEN_SERVER_CITY );
170 
171         // [this server country as QLocale ID]
172         bool      ok;
173         const int iCountry = slServInfoSeparateParams[2].toInt ( &ok );
174         if ( ok && iCountry >= 0 && iCountry <= QLocale::LastCountry )
175         {
176             ThisServerListEntry.eCountry = static_cast<QLocale::Country> ( iCountry );
177         }
178     }
179 
180     // per definition, the very first entry is this server and this entry will
181     // never be deleted
182     ServerList.clear();
183 
184     // per definition, the first entry in the server list is the own server
185     ServerList.append ( ThisServerListEntry );
186 
187     // Clear the persistent serverlist file name
188     ServerListFileName.clear();
189 
190     if ( bIsCentralServer )
191     {
192         // Load any persistent server list (create it if it is not there)
193         if ( !strServerListFileName.isEmpty() && ServerListFileName.isEmpty() )
194         {
195             CentralServerLoadServerList ( strServerListFileName );
196         }
197 
198         // whitelist parsing
199         if ( !strServerListFilter.isEmpty() )
200         {
201             // split the different parameter strings
202             QStringList  slWhitelistAddresses = strServerListFilter.split ( ";" );
203             QHostAddress CurWhiteListAddress;
204 
205             for ( int iIdx = 0; iIdx < slWhitelistAddresses.size(); iIdx++ )
206             {
207                 // check for special case: [version]
208                 if ( ( slWhitelistAddresses.at ( iIdx ).length() > 2 ) && ( slWhitelistAddresses.at ( iIdx ).left ( 1 ) == "[" ) &&
209                      ( slWhitelistAddresses.at ( iIdx ).right ( 1 ) == "]" ) )
210                 {
211                     strMinServerVersion = slWhitelistAddresses.at ( iIdx ).mid ( 1, slWhitelistAddresses.at ( iIdx ).length() - 2 );
212                 }
213                 else if ( CurWhiteListAddress.setAddress ( slWhitelistAddresses.at ( iIdx ) ) )
214                 {
215                     vWhiteList << CurWhiteListAddress;
216                     qInfo() << qUtf8Printable ( QString ( "Whitelist entry added: %1" ).arg ( CurWhiteListAddress.toString() ) );
217                 }
218             }
219         }
220     }
221 
222     // for slave servers start the one shot timer for determining if it is a
223     // permanent server
224     if ( !bIsCentralServer )
225     {
226         // 1 minute = 60 * 1000 ms
227         QTimer::singleShot ( SERVLIST_TIME_PERMSERV_MINUTES * 60000, this, SLOT ( OnTimerIsPermanent() ) );
228     }
229 
230     // prepare the register server response timer (single shot timer)
231     TimerCLRegisterServerResp.setSingleShot ( true );
232     TimerCLRegisterServerResp.setInterval ( REGISTER_SERVER_TIME_OUT_MS );
233 
234     // Connections -------------------------------------------------------------
235     QObject::connect ( &TimerPollList, &QTimer::timeout, this, &CServerListManager::OnTimerPollList );
236 
237     QObject::connect ( &TimerPingServerInList, &QTimer::timeout, this, &CServerListManager::OnTimerPingServerInList );
238 
239     QObject::connect ( &TimerPingCentralServer, &QTimer::timeout, this, &CServerListManager::OnTimerPingCentralServer );
240 
241     QObject::connect ( &TimerRegistering, &QTimer::timeout, this, &CServerListManager::OnTimerRegistering );
242 
243     QObject::connect ( &TimerCLRegisterServerResp, &QTimer::timeout, this, &CServerListManager::OnTimerCLRegisterServerResp );
244 
245     QObject::connect ( QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &CServerListManager::OnAboutToQuit );
246 }
247 
SetCentralServerAddress(const QString sNCentServAddr)248 void CServerListManager::SetCentralServerAddress ( const QString sNCentServAddr )
249 {
250     // if the address has not actually changed, do nothing
251     if ( sNCentServAddr == strCentralServerAddress )
252     {
253         return;
254     }
255 
256     // if we are registered to a custom directory server, unregister before updating the name
257     if ( eCentralServerAddressType == AT_CUSTOM && GetSvrRegStatus() == SRS_REGISTERED )
258     {
259         SlaveServerUnregister();
260     }
261 
262     QMutexLocker locker ( &Mutex );
263 
264     // now save the new name
265     strCentralServerAddress = sNCentServAddr;
266 
267     SetCentralServerState();
268 }
269 
SetCentralServerAddressType(const ECSAddType eNCSAT)270 void CServerListManager::SetCentralServerAddressType ( const ECSAddType eNCSAT )
271 {
272     // if the type is changing, unregister before updating
273     if ( eNCSAT != eCentralServerAddressType && GetSvrRegStatus() == SRS_REGISTERED )
274     {
275         SlaveServerUnregister();
276     }
277 
278     QMutexLocker locker ( &Mutex );
279 
280     // now update the server type
281     eCentralServerAddressType = eNCSAT;
282 
283     SetCentralServerState();
284 }
285 
SetCentralServerState()286 void CServerListManager::SetCentralServerState()
287 {
288     // per definition: If we are in server mode and the directory server address
289     // is the localhost address, and set to Custom, we are in directory server mode.
290     bool bNCentralServer =
291         ( ( !strCentralServerAddress.compare ( "localhost", Qt::CaseInsensitive ) || !strCentralServerAddress.compare ( "127.0.0.1" ) ) &&
292           ( eCentralServerAddressType == AT_CUSTOM ) );
293     if ( bIsCentralServer != bNCentralServer )
294     {
295         bIsCentralServer = bNCentralServer;
296         qInfo() << ( bIsCentralServer ? "Now a directory server" : "No longer a directory server" );
297     }
298 }
299 
Update()300 void CServerListManager::Update()
301 {
302     QMutexLocker locker ( &Mutex );
303 
304     if ( bEnabled )
305     {
306         if ( bIsCentralServer )
307         {
308             // start timer for polling the server list if enabled
309             // 1 minute = 60 * 1000 ms
310             TimerPollList.start ( SERVLIST_POLL_TIME_MINUTES * 60000 );
311 
312             // start timer for sending ping messages to servers in the list
313             TimerPingServerInList.start ( SERVLIST_UPDATE_PING_SERVERS_MS );
314         }
315         else
316         {
317             // initiate registration right away so that we do not have to wait
318             // for the first time out of the timer until the slave server gets
319             // registered at the directory server, note that we have to unlock
320             // the mutex before calling the function since inside this function
321             // the mutex is locked, too
322             locker.unlock();
323             {
324                 OnTimerRegistering();
325             }
326             locker.relock();
327 
328             // reset the retry counter to zero because update was called
329             iSvrRegRetries = 0;
330 
331             // start timer for registration timeout
332             TimerCLRegisterServerResp.start();
333 
334             // start timer for registering this server at the directory server
335             // 1 minute = 60 * 1000 ms
336             TimerRegistering.start ( SERVLIST_REGIST_INTERV_MINUTES * 60000 );
337 
338             // Start timer for ping the directory server in short intervals to
339             // keep the port open at the NAT router.
340             // If no NAT is used, we send the messages anyway since they do
341             // not hurt (very low traffic). We also reuse the same update
342             // time as used in the directory server for pinging the slave
343             // servers.
344             TimerPingCentralServer.start ( SERVLIST_UPDATE_PING_SERVERS_MS );
345         }
346     }
347     else
348     {
349         // disable service -> stop timer
350         if ( bIsCentralServer )
351         {
352             TimerPollList.stop();
353             TimerPingServerInList.stop();
354         }
355         else
356         {
357             TimerCLRegisterServerResp.stop();
358             TimerRegistering.stop();
359             TimerPingCentralServer.stop();
360         }
361     }
362 }
363 
364 /* Directory server list functionality ****************************************/
OnTimerPingServerInList()365 void CServerListManager::OnTimerPingServerInList()
366 {
367     QMutexLocker locker ( &Mutex );
368 
369     const int iCurServerListSize = ServerList.size();
370 
371     // send ping to list entries except of the very first one (which is the directory
372     // server entry)
373     for ( int iIdx = 1; iIdx < iCurServerListSize; iIdx++ )
374     {
375         // send empty message to keep NAT port open at slave server
376         pConnLessProtocol->CreateCLEmptyMes ( ServerList[iIdx].HostAddr );
377     }
378 }
379 
OnTimerPollList()380 void CServerListManager::OnTimerPollList()
381 {
382     CVector<CHostAddress> vecRemovedHostAddr;
383 
384     QMutexLocker locker ( &Mutex );
385 
386     // Check all list entries are still valid (omitting the directory server itself)
387     for ( int iIdx = ServerList.size() - 1; iIdx > 0; iIdx-- )
388     {
389         // 1 minute = 60 * 1000 ms
390         if ( ServerList[iIdx].RegisterTime.elapsed() > ( SERVLIST_TIME_OUT_MINUTES * 60000 ) )
391         {
392             // remove this list entry
393             vecRemovedHostAddr.Add ( ServerList[iIdx].HostAddr );
394             ServerList.removeAt ( iIdx );
395         }
396     }
397 
398     locker.unlock();
399 
400     foreach ( const CHostAddress HostAddr, vecRemovedHostAddr )
401     {
402         qInfo() << qUtf8Printable ( QString ( "Expired entry for %1" ).arg ( HostAddr.toString() ) );
403     }
404 }
405 
CentralServerRegisterServer(const CHostAddress & InetAddr,const CHostAddress & LInetAddr,const CServerCoreInfo & ServerInfo,const QString strVersion)406 void CServerListManager::CentralServerRegisterServer ( const CHostAddress&    InetAddr,
407                                                        const CHostAddress&    LInetAddr,
408                                                        const CServerCoreInfo& ServerInfo,
409                                                        const QString          strVersion )
410 {
411     if ( bIsCentralServer && bEnabled )
412     {
413         qInfo() << qUtf8Printable ( QString ( "Requested to register entry for %1 (%2): %3" )
414                                         .arg ( InetAddr.toString() )
415                                         .arg ( LInetAddr.toString() )
416                                         .arg ( ServerInfo.strName ) );
417 
418         // check for minimum server version
419         if ( !strMinServerVersion.isEmpty() )
420         {
421 #if ( QT_VERSION >= QT_VERSION_CHECK( 5, 6, 0 ) )
422             if ( strVersion.isEmpty() ||
423                  QVersionNumber::compare ( QVersionNumber::fromString ( strMinServerVersion ), QVersionNumber::fromString ( strVersion ) ) > 0 )
424             {
425                 pConnLessProtocol->CreateCLRegisterServerResp ( InetAddr, SRR_VERSION_TOO_OLD );
426                 return; // leave function early, i.e., we do not register this server
427             }
428 #endif
429         }
430 
431         // check for whitelist (it is enabled if it is not empty per definition)
432         if ( !vWhiteList.empty() )
433         {
434             // if the server is not listed, refuse registration and send registration response
435             if ( !vWhiteList.contains ( InetAddr.InetAddr ) )
436             {
437                 pConnLessProtocol->CreateCLRegisterServerResp ( InetAddr, SRR_NOT_FULFILL_REQIREMENTS );
438                 return; // leave function early, i.e., we do not register this server
439             }
440         }
441 
442         // access/modifications to the server list needs to be mutexed
443         QMutexLocker locker ( &Mutex );
444 
445         const int iCurServerListSize = ServerList.size();
446 
447         // Check if server is already registered.
448         // The very first list entry must not be checked since
449         // this is per definition the directory server (i.e., this server)
450         int iSelIdx = IndexOf ( InetAddr );
451 
452         // if server is not yet registered, we have to create a new entry
453         if ( iSelIdx == INVALID_INDEX )
454         {
455             // check for maximum allowed number of servers in the server list
456             if ( iCurServerListSize < MAX_NUM_SERVERS_IN_SERVER_LIST )
457             {
458                 // create a new server list entry and init with received data
459                 ServerList.append ( CServerListEntry ( InetAddr, LInetAddr, ServerInfo ) );
460                 iSelIdx = iCurServerListSize;
461             }
462         }
463         else
464         {
465             // update all data and call update registration function
466             ServerList[iSelIdx].LHostAddr        = LInetAddr;
467             ServerList[iSelIdx].strName          = ServerInfo.strName;
468             ServerList[iSelIdx].eCountry         = ServerInfo.eCountry;
469             ServerList[iSelIdx].strCity          = ServerInfo.strCity;
470             ServerList[iSelIdx].iMaxNumClients   = ServerInfo.iMaxNumClients;
471             ServerList[iSelIdx].bPermanentOnline = ServerInfo.bPermanentOnline;
472 
473             ServerList[iSelIdx].UpdateRegistration();
474         }
475 
476         pConnLessProtocol->CreateCLRegisterServerResp ( InetAddr,
477                                                         iSelIdx == INVALID_INDEX ? ESvrRegResult::SRR_CENTRAL_SVR_FULL
478                                                                                  : ESvrRegResult::SRR_REGISTERED );
479     }
480 }
481 
CentralServerUnregisterServer(const CHostAddress & InetAddr)482 void CServerListManager::CentralServerUnregisterServer ( const CHostAddress& InetAddr )
483 {
484     if ( bIsCentralServer && bEnabled )
485     {
486         qInfo() << qUtf8Printable ( QString ( "Requested to unregister entry for %1" ).arg ( InetAddr.toString() ) );
487 
488         QMutexLocker locker ( &Mutex );
489 
490         // Find the server to unregister in the list. The very first list entry
491         // must not be removed since this is per definition the directory server
492         // (i.e., this server).
493         int iIdx = IndexOf ( InetAddr );
494         if ( iIdx > 0 )
495         {
496             ServerList.removeAt ( iIdx );
497         }
498     }
499 }
500 
CentralServerQueryServerList(const CHostAddress & InetAddr)501 void CServerListManager::CentralServerQueryServerList ( const CHostAddress& InetAddr )
502 {
503     QMutexLocker locker ( &Mutex );
504 
505     if ( bIsCentralServer && bEnabled )
506     {
507         const int iCurServerListSize = ServerList.size();
508 
509         // allocate memory for the entire list
510         CVector<CServerInfo> vecServerInfo ( iCurServerListSize );
511 
512         // copy the list (we have to copy it since the message requires
513         // a vector but the list is actually stored in a QList object and
514         // not in a vector object
515         for ( int iIdx = 0; iIdx < iCurServerListSize; iIdx++ )
516         {
517             // copy list item
518             vecServerInfo[iIdx] = ServerList[iIdx];
519 
520             if ( iIdx > 0 )
521             {
522                 // check if the address of the client which is requesting the
523                 // list is the same address as one server in the list -> in this
524                 // case he has to connect to the local host address and port
525                 // to allow for NAT.
526                 if ( vecServerInfo[iIdx].HostAddr.InetAddr == InetAddr.InetAddr )
527                 {
528                     vecServerInfo[iIdx].HostAddr = ServerList[iIdx].LHostAddr;
529                 }
530                 else if ( !NetworkUtil::IsPrivateNetworkIP ( InetAddr.InetAddr ) &&
531                           NetworkUtil::IsPrivateNetworkIP ( vecServerInfo[iIdx].HostAddr.InetAddr ) &&
532                           !NetworkUtil::IsPrivateNetworkIP ( ServerList[iIdx].LHostAddr.InetAddr ) )
533                 {
534                     // We've got a request from a public client, the server
535                     // list's entry's primary address is a private address,
536                     // but it supplied an additional public address using
537                     // --serverpublicip.
538                     // In this case, use the latter.
539                     // This is common when running a directory server with slave
540                     // servers behind a NAT and dealing with external, public
541                     // clients.
542                     vecServerInfo[iIdx].HostAddr = ServerList[iIdx].LHostAddr;
543                     // ?? Shouldn't this send the ping, as below ??
544                 }
545                 else
546                 {
547                     // create "send empty message" for all registered servers
548                     // (except of the very first list entry since this is this
549                     // server (directory server) per definition) and also it is
550                     // not required to send this message, if the server is on
551                     // the same computer
552                     pConnLessProtocol->CreateCLSendEmptyMesMes ( vecServerInfo[iIdx].HostAddr, InetAddr );
553                 }
554             }
555         }
556 
557         // send the server list to the client, since we do not know that the client
558         // has a UDP fragmentation issue, we send both lists, the reduced and the
559         // normal list after each other
560         pConnLessProtocol->CreateCLRedServerListMes ( InetAddr, vecServerInfo );
561         pConnLessProtocol->CreateCLServerListMes ( InetAddr, vecServerInfo );
562     }
563 }
564 
IndexOf(CHostAddress haSearchTerm)565 int CServerListManager::IndexOf ( CHostAddress haSearchTerm )
566 {
567     // Find the server in the list. The very first list entry
568     // per definition is the directory server
569     // (i.e., this server).
570     for ( int iIdx = ServerList.size() - 1; iIdx > 0; iIdx-- )
571     {
572         if ( ServerList[iIdx].HostAddr == haSearchTerm )
573         {
574             return iIdx;
575         }
576     }
577     return INVALID_INDEX;
578 }
579 
CentralServerLoadServerList(const QString strServerList)580 void CServerListManager::CentralServerLoadServerList ( const QString strServerList )
581 {
582     QFile file ( strServerList );
583 
584     if ( !file.open ( QIODevice::ReadWrite | QIODevice::Text ) )
585     {
586         qWarning() << qUtf8Printable (
587             QString ( "Could not open '%1' for read/write.  Please check that Jamulus has permission (and that there is free space)." )
588                 .arg ( ServerListFileName ) );
589         return;
590     }
591 
592     // use entire file content for the persistent server list
593     // and remember it for later writing
594     qInfo() << "Setting persistent server list file:" << strServerList;
595     ServerListFileName = strServerList;
596     CHostAddress haServerHostAddr;
597 
598     QTextStream in ( &file );
599     while ( !in.atEnd() )
600     {
601         QString     line   = in.readLine();
602         QStringList slLine = line.split ( ";" );
603         if ( slLine.count() != 7 )
604         {
605             qWarning() << qUtf8Printable ( QString ( "Could not parse '%1' successfully - bad line" ).arg ( line ) );
606             continue;
607         }
608 
609         NetworkUtil::ParseNetworkAddress ( slLine[0], haServerHostAddr, bEnableIPv6 );
610         int iIdx = IndexOf ( haServerHostAddr );
611         if ( iIdx != INVALID_INDEX )
612         {
613             qWarning() << qUtf8Printable ( QString ( "Skipping '%1' - duplicate host %2" ).arg ( line ).arg ( haServerHostAddr.toString() ) );
614             continue;
615         }
616 
617         CServerListEntry serverListEntry =
618             CServerListEntry::parse ( slLine[0], slLine[1], slLine[2], slLine[3], slLine[4], slLine[5], slLine[6].toInt() != 0, bEnableIPv6 );
619 
620         // We expect servers to have addresses...
621         if ( ( CHostAddress() == serverListEntry.HostAddr ) )
622         {
623             qWarning() << qUtf8Printable ( QString ( "Could not parse '%1' successfully - invalid host" ).arg ( line ) );
624             continue;
625         }
626 
627         qInfo() << qUtf8Printable ( QString ( "Loading registration for %1 (%2): %3" )
628                                         .arg ( serverListEntry.HostAddr.toString() )
629                                         .arg ( serverListEntry.LHostAddr.toString() )
630                                         .arg ( serverListEntry.strName ) );
631         ServerList.append ( serverListEntry );
632     }
633 }
634 
CentralServerSaveServerList()635 void CServerListManager::CentralServerSaveServerList()
636 {
637     if ( ServerListFileName.isEmpty() )
638     {
639         return;
640     }
641 
642     QFile file ( ServerListFileName );
643 
644     if ( !file.open ( QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text ) )
645     {
646         // Not a useable file
647         qWarning() << QString ( tr ( "Could not write to '%1'" ) ).arg ( ServerListFileName );
648         ServerListFileName.clear();
649 
650         return;
651     }
652 
653     QTextStream out ( &file );
654     // This loop *deliberately* omits the first element in the list
655     // (that's this server, which is added automatically on start up, not read)
656     for ( int iIdx = ServerList.size() - 1; iIdx > 0; iIdx-- )
657     {
658         qInfo() << qUtf8Printable ( QString ( "Saving registration for %1 (%2): %3" )
659                                         .arg ( ServerList[iIdx].HostAddr.toString() )
660                                         .arg ( ServerList[iIdx].LHostAddr.toString() )
661                                         .arg ( ServerList[iIdx].strName ) );
662         out << ServerList[iIdx].toCSV() << "\n";
663     }
664 }
665 
666 /* Slave server functionality *************************************************/
StoreRegistrationResult(ESvrRegResult eResult)667 void CServerListManager::StoreRegistrationResult ( ESvrRegResult eResult )
668 {
669     // we need the lock since the user might change the server properties at
670     // any time so another response could arrive
671     QMutexLocker locker ( &Mutex );
672 
673     // we got some response, so stop the retry timer
674     TimerCLRegisterServerResp.stop();
675 
676     switch ( eResult )
677     {
678     case ESvrRegResult::SRR_REGISTERED:
679         SetSvrRegStatus ( ESvrRegStatus::SRS_REGISTERED );
680         break;
681 
682     case ESvrRegResult::SRR_CENTRAL_SVR_FULL:
683         SetSvrRegStatus ( ESvrRegStatus::SRS_CENTRAL_SVR_FULL );
684         break;
685 
686     case ESvrRegResult::SRR_VERSION_TOO_OLD:
687         SetSvrRegStatus ( ESvrRegStatus::SRS_VERSION_TOO_OLD );
688         break;
689 
690     case ESvrRegResult::SRR_NOT_FULFILL_REQIREMENTS:
691         SetSvrRegStatus ( ESvrRegStatus::SRS_NOT_FULFILL_REQUIREMENTS );
692         break;
693 
694     default:
695         SetSvrRegStatus ( ESvrRegStatus::SRS_UNKNOWN_RESP );
696         break;
697     }
698 }
699 
OnTimerPingCentralServer()700 void CServerListManager::OnTimerPingCentralServer()
701 {
702     QMutexLocker locker ( &Mutex );
703 
704     // first check if directory server address is valid
705     if ( !( SlaveCurCentServerHostAddress == CHostAddress() ) )
706     {
707         // send empty message to directory server to keep NAT port open -> we do
708         // not require any answer from the directory server
709         pConnLessProtocol->CreateCLEmptyMes ( SlaveCurCentServerHostAddress );
710     }
711 }
712 
OnTimerCLRegisterServerResp()713 void CServerListManager::OnTimerCLRegisterServerResp()
714 {
715     QMutexLocker locker ( &Mutex );
716 
717     if ( eSvrRegStatus == SRS_REQUESTED )
718     {
719         iSvrRegRetries++;
720 
721         if ( iSvrRegRetries >= REGISTER_SERVER_RETRY_LIMIT )
722         {
723             SetSvrRegStatus ( SRS_TIME_OUT );
724         }
725         else
726         {
727             locker.unlock();
728             {
729                 OnTimerRegistering();
730             }
731             locker.relock();
732 
733             // re-start timer for registration timeout
734             TimerCLRegisterServerResp.start();
735         }
736     }
737 }
738 
SlaveServerRegisterServer(const bool bIsRegister)739 void CServerListManager::SlaveServerRegisterServer ( const bool bIsRegister )
740 {
741     // we need the lock since the user might change the server properties at
742     // any time
743     QMutexLocker locker ( &Mutex );
744 
745     // get the correct directory server address
746     const QString strCurCentrServAddr = NetworkUtil::GetCentralServerAddress ( eCentralServerAddressType, strCentralServerAddress );
747 
748     // For the slave server, the slave server properties are stored in the
749     // very first item in the server list (which is actually no server list
750     // but just one item long for the slave server).
751     // Note that we always have to parse the server address again since if
752     // it is an URL of a dynamic IP address, the IP address might have
753     // changed in the meanwhile.
754 
755     // Allow IPv4 only for communicating with Directory Servers
756     if ( NetworkUtil().ParseNetworkAddress ( strCurCentrServAddr, SlaveCurCentServerHostAddress, false ) )
757     {
758         if ( bIsRegister )
759         {
760             // register server
761             SetSvrRegStatus ( SRS_REQUESTED );
762 
763             pConnLessProtocol->CreateCLRegisterServerExMes ( SlaveCurCentServerHostAddress, SlaveCurLocalHostAddress, ServerList[0] );
764         }
765         else
766         {
767             // unregister server
768             SetSvrRegStatus ( SRS_UNREGISTERED );
769 
770             pConnLessProtocol->CreateCLUnregisterServerMes ( SlaveCurCentServerHostAddress );
771         }
772     }
773     else
774     {
775         SetSvrRegStatus ( SRS_BAD_ADDRESS );
776     }
777 }
778 
SetSvrRegStatus(ESvrRegStatus eNSvrRegStatus)779 void CServerListManager::SetSvrRegStatus ( ESvrRegStatus eNSvrRegStatus )
780 {
781     // output regirstation result/update on the console
782     qInfo() << qUtf8Printable ( QString ( "Server Registration Status update: %1" ).arg ( svrRegStatusToString ( eNSvrRegStatus ) ) );
783 
784     // store the state and inform the GUI about the new status
785     eSvrRegStatus = eNSvrRegStatus;
786     emit SvrRegStatusChanged();
787 }
788