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