2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 #include "ServerList.h" // Interface declarations.
30 #include <protocol/Protocols.h>
31 #include <protocol/ed2k/Constants.h>
32 #include <common/DataFileVersion.h>
33 #include <tags/ServerTags.h>
35 #include <wx/txtstrm.h>
36 #include <wx/wfstream.h>
37 #include <wx/url.h> // Needed for wxURL
38 #include <wx/tokenzr.h>
40 #include "DownloadQueue.h" // Needed for CDownloadQueue
41 #include "ServerConnect.h" // Needed for CServerConnect
42 #include "Server.h" // Needed for CServer and SRV_PR_*
43 #include "OtherStructs.h" // Needed for ServerMet_Struct
44 #include "CFile.h" // Needed for CFile
45 #include "HTTPDownload.h" // Needed for HTTPThread
46 #include "Preferences.h" // Needed for thePrefs
47 #include "amule.h" // Needed for theApp
48 #include "Statistics.h" // Needed for theStats
49 #include "Packet.h" // Neeed for CPacket
51 #include "ScopedPtr.h"
52 #include <common/Format.h>
54 #include <common/FileFunctions.h> // Needed for UnpackArchive
55 #include <common/TextFile.h> // Needed for CTextFile
57 CServerList::CServerList()
59 m_serverpos
= m_servers
.end();
60 m_statserverpos
= m_servers
.end();
61 m_nLastED2KServerLinkCheck
= ::GetTickCount();
62 m_initialized
= false;
66 bool CServerList::Init()
69 bool bRes
= LoadServerMet(CPath(theApp
->ConfigDir
+ wxT("server.met")));
71 // insert static servers from textfile
72 m_staticServersConfig
= theApp
->ConfigDir
+ wxT("staticservers.dat");
75 // Send the auto-update of server.met via HTTPThread requests
76 current_url_index
= 0;
77 if ( thePrefs::AutoServerlist()) {
86 bool CServerList::LoadServerMet(const CPath
& path
)
88 AddLogLineN(CFormat(_("Loading server.met file: %s")) % path
);
90 bool merge
= !m_servers
.empty();
92 if (!path
.FileExists()) {
93 AddLogLineN(_("Server.met file not found!"));
97 // Try to unpack the file, might be an archive
98 const wxChar
* mets
[] = { wxT("server.met"), NULL
};
99 // Try to unpack the file, might be an archive
100 if (UnpackArchive(path
, mets
).second
!= EFT_Met
) {
101 AddLogLineC(CFormat(_("Failed to load server.met file '%s', unknown format encountered.")) % path
);
105 CFile
servermet(path
, CFile::read
);
106 if ( !servermet
.IsOpened() ){
107 AddLogLineN(_("Failed to open server.met!") );
113 Notify_ServerFreeze();
115 byte version
= servermet
.ReadUInt8();
117 if (version
!= 0xE0 && version
!= MET_HEADER
) {
118 AddLogLineC(CFormat(_("Server.met file corrupt, found invalid versiontag: 0x%x, size %i")) % version
% sizeof(version
));
123 uint32 fservercount
= servermet
.ReadUInt32();
125 ServerMet_Struct sbuffer
;
126 uint32 iAddCount
= 0;
128 for ( uint32 j
= 0; j
< fservercount
; ++j
) {
129 sbuffer
.ip
= servermet
.ReadUInt32();
130 sbuffer
.port
= servermet
.ReadUInt16();
131 sbuffer
.tagcount
= servermet
.ReadUInt32();
133 CServer
* newserver
= new CServer(&sbuffer
);
136 for ( uint32 i
= 0; i
< sbuffer
.tagcount
; ++i
) {
137 newserver
->AddTagFromFile(&servermet
);
140 // Server priorities are not in sorted order
141 // High = 1, Low = 2, Normal = 0, so we have to check
142 // in a less logical fashion.
143 int priority
= newserver
->GetPreferences();
144 if (priority
< SRV_PR_MIN
|| priority
> SRV_PR_MAX
) {
145 newserver
->SetPreference(SRV_PR_NORMAL
);
148 // set listname for server
149 if ( newserver
->GetListName().IsEmpty() ) {
150 newserver
->SetListName(wxT("Server ") +newserver
->GetAddress());
154 if ( !theApp
->AddServer(newserver
) ) {
155 CServer
* update
= GetServerByAddress(newserver
->GetAddress(), newserver
->GetPort());
157 update
->SetListName( newserver
->GetListName());
158 if(!newserver
->GetDescription().IsEmpty()) {
159 update
->SetDescription( newserver
->GetDescription());
161 Notify_ServerRefresh(update
);
172 // cppcheck-suppress duplicateBranch
174 AddLogLineC(CFormat(wxPLURAL("%i server in server.met found", "%i servers in server.met found", fservercount
)) % fservercount
);
176 AddLogLineC(CFormat(wxPLURAL("%d server added", "%d servers added", iAddCount
)) % iAddCount
);
178 } catch (const CInvalidPacket
& err
) {
179 AddLogLineC(_("Error: the file 'server.met' is corrupted: ") + err
.what());
182 } catch (const CSafeIOException
& err
) {
183 AddLogLineC(_("IO error while reading 'server.met': ") + err
.what());
192 bool CServerList::AddServer(CServer
* in_server
, bool fromUser
)
194 if ( !in_server
->GetPort() ) {
196 AddLogLineC(CFormat( _("Server not added: [%s:%d] does not specify a valid port.") )
197 % in_server
->GetAddress()
198 % in_server
->GetPort()
204 !in_server
->HasDynIP() &&
206 !IsGoodIP( in_server
->GetIP(), thePrefs::FilterLanIPs() ) ||
207 ( // don't test for filtered while ipfilter is still loading,
208 // it will be filtered when filter loading is finished
209 theApp
->ipfilter
->IsReady()
210 && theApp
->ipfilter
->IsFiltered(in_server
->GetIP(), true))
214 AddLogLineC(CFormat( _("Server not added: The IP of [%s:%d] is filtered or invalid.") )
215 % in_server
->GetAddress()
216 % in_server
->GetPort()
223 CServer
* test_server
= GetServerByAddress(in_server
->GetAddress(), in_server
->GetPort());
224 // Avoid duplicate (dynIP) servers: If the server which is to be added, is a dynIP-server
225 // but we don't know yet it's DN, we need to search for an already available server with
227 if (test_server
== NULL
&& in_server
->GetIP() != 0) {
228 test_server
= GetServerByIPTCP(in_server
->GetIP(), in_server
->GetPort());
233 AddLogLineC(CFormat( _("Server not added: Server with matching IP:Port [%s:%d] found in list.") )
234 % in_server
->GetAddress()
235 % in_server
->GetPort()
239 test_server
->ResetFailedCount();
240 Notify_ServerRefresh( test_server
);
245 theStats::AddServer();
247 m_servers
.push_back(in_server
);
248 NotifyObservers( EventType( EventType::INSERTED
, in_server
) );
251 AddLogLineC(CFormat( _("Server added: Server at [%s:%d] using the name '%s'.") )
252 % in_server
->GetAddress()
253 % in_server
->GetPort()
254 % in_server
->GetListName()
263 void CServerList::ServerStats()
265 uint32 tNow
= ::GetTickCount();
267 if (theApp
->IsConnectedED2K() && !m_servers
.empty()) {
268 CServer
* ping_server
= GetNextStatServer();
269 CServer
* test
= ping_server
;
274 while (ping_server
->GetLastPingedTime() && (tNow
- ping_server
->GetLastPingedTime()) < UDPSERVSTATREASKTIME
) {
275 ping_server
= GetNextStatServer();
276 if (ping_server
== test
) {
281 if (ping_server
->GetFailedCount() >= thePrefs::GetDeadserverRetries() && thePrefs::DeadServer() && !ping_server
->IsStaticMember()) {
282 RemoveServer(ping_server
);
286 srand((unsigned)time(NULL
));
287 ping_server
->SetRealLastPingedTime(tNow
); // this is not used to calcualte the next ping, but only to ensure a minimum delay for premature pings
288 if (!ping_server
->GetCryptPingReplyPending() && (!ping_server
->GetLastPingedTime() || (tNow
- ping_server
->GetLastPingedTime()) >= UDPSERVSTATREASKTIME
) && theApp
->GetPublicIP() && thePrefs::IsServerCryptLayerUDPEnabled()) {
289 // We try a obfsucation ping first and wait 20 seconds for an answer
290 // if it doesn't get responsed, we don't count it as error but continue with a normal ping
291 ping_server
->SetCryptPingReplyPending(true);
292 uint32 nPacketLen
= 4 + (uint8
)(rand() % 16); // max padding 16 bytes
293 CScopedArray
<byte
> pRawPacket(nPacketLen
);
294 uint32 dwChallenge
= (rand() << 17) | (rand() << 2) | (rand() & 0x03);
295 if (dwChallenge
== 0) {
299 memcpy(pRawPacket
.get(), &dwChallenge
, sizeof(uint32
));
300 for (uint32 i
= 4; i
< nPacketLen
; i
++) { // fillng up the remaining bytes with random data
301 pRawPacket
[i
] = (uint8
)rand();
304 ping_server
->SetChallenge(dwChallenge
);
305 ping_server
->SetLastPinged(tNow
);
306 ping_server
->SetLastPingedTime((tNow
- (uint32
)UDPSERVSTATREASKTIME
) + 20); // give it 20 seconds to respond
308 AddDebugLogLineN(logServerUDP
, CFormat(wxT(">> Sending OP__GlobServStatReq (obfuscated) to server %s:%u")) % ping_server
->GetAddress() % ping_server
->GetPort());
310 CPacket
* packet
= new CPacket(pRawPacket
[1], nPacketLen
- 2, pRawPacket
[0]);
311 packet
->CopyToDataBuffer(0, pRawPacket
.get() + 2, nPacketLen
- 2);
313 theStats::AddUpOverheadServer(packet
->GetPacketSize());
314 theApp
->serverconnect
->SendUDPPacket(packet
, ping_server
, true, true /*raw packet*/, 12 /* Port offset is 12 for obfuscated encryption*/);
315 } else if (ping_server
->GetCryptPingReplyPending() || theApp
->GetPublicIP() == 0 || !thePrefs::IsServerCryptLayerUDPEnabled()){
316 // our obfsucation ping request was not answered, so probably the server doesn'T supports obfuscation
317 // continue with a normal request
318 if (ping_server
->GetCryptPingReplyPending() && thePrefs::IsServerCryptLayerUDPEnabled()) {
319 AddDebugLogLineN(logServerUDP
, wxT("CryptPing failed for server ") + ping_server
->GetListName());
320 } else if (thePrefs::IsServerCryptLayerUDPEnabled()) {
321 AddDebugLogLineN(logServerUDP
, wxT("CryptPing skipped because our public IP is unknown for server ") + ping_server
->GetListName());
324 ping_server
->SetCryptPingReplyPending(false);
326 CPacket
* packet
= new CPacket(OP_GLOBSERVSTATREQ
, 4, OP_EDONKEYPROT
);
327 uint32 challenge
= 0x55AA0000 + (uint16
)rand();
328 ping_server
->SetChallenge(challenge
);
329 packet
->CopyUInt32ToDataBuffer(challenge
);
330 ping_server
->SetLastPinged(tNow
);
331 ping_server
->SetLastPingedTime(tNow
- (rand() % HR2S(1)));
332 ping_server
->AddFailedCount();
333 Notify_ServerRefresh(ping_server
);
334 theStats::AddUpOverheadServer(packet
->GetPacketSize());
335 theApp
->serverconnect
->SendUDPPacket(packet
, ping_server
, true);
343 void CServerList::RemoveServer(CServer
* in_server
)
345 if (in_server
== theApp
->serverconnect
->GetCurrentServer()) {
346 theApp
->ShowAlert(_("You are connected to the server you are trying to delete. please disconnect first."), _("Info"), wxOK
);
348 CInternalList::iterator it
= std::find(m_servers
.begin(), m_servers
.end(), in_server
);
349 if ( it
!= m_servers
.end() ) {
350 if (theApp
->downloadqueue
->GetUDPServer() == in_server
) {
351 theApp
->downloadqueue
->SetUDPServer( 0 );
354 NotifyObservers( EventType( EventType::REMOVED
, in_server
) );
356 if (m_serverpos
== it
) {
359 if (m_statserverpos
== it
) {
363 theStats::DeleteServer();
365 Notify_ServerRemove(in_server
);
372 void CServerList::RemoveAllServers()
374 NotifyObservers( EventType( EventType::CLEARED
) );
376 theStats::DeleteAllServers();
377 // no connection, safely remove all servers
378 while ( !m_servers
.empty() ) {
379 delete m_servers
.back();
380 m_servers
.pop_back();
382 m_serverpos
= m_servers
.end();
383 m_statserverpos
= m_servers
.end();
387 void CServerList::GetStatus(uint32
&failed
, uint32
&user
, uint32
&file
, uint32
&tuser
, uint32
&tfile
,float &occ
)
398 for (CInternalList::const_iterator it
= m_servers
.begin(); it
!= m_servers
.end(); ++it
) {
399 const CServer
* const curr
= *it
;
400 if( curr
->GetFailedCount() ) {
403 user
+= curr
->GetUsers();
404 file
+= curr
->GetFiles();
406 tuser
+= curr
->GetUsers();
407 tfile
+= curr
->GetFiles();
409 if (curr
->GetMaxUsers()) {
410 tuserk
+= curr
->GetUsers(); // total users on servers with known maximum
411 maxusers
+=curr
->GetMaxUsers();
415 occ
=(float)(tuserk
*100)/maxusers
;
420 void CServerList::GetUserFileStatus(uint32
&user
, uint32
&file
)
424 for (CInternalList::const_iterator it
= m_servers
.begin(); it
!= m_servers
.end(); ++it
) {
425 const CServer
* const curr
= *it
;
426 if( !curr
->GetFailedCount() ) {
427 user
+= curr
->GetUsers();
428 file
+= curr
->GetFiles();
434 CServerList::~CServerList()
436 if (thePrefs::GetNetworkED2K() && IsInitialized()) {
439 while ( !m_servers
.empty() ) {
440 delete m_servers
.back();
441 m_servers
.pop_back();
446 void CServerList::LoadStaticServers()
448 if ( !CPath::FileExists(m_staticServersConfig
) ) {
452 wxFileInputStream
stream(m_staticServersConfig
);
453 wxTextInputStream
f(stream
);
455 while ( !stream
.Eof() ) {
456 wxString line
= f
.ReadLine();
459 if (line
.IsEmpty() || line
.GetChar(0) == '#' || line
.GetChar(0) == '/') {
463 wxStringTokenizer
tokens( line
, wxT(",") );
465 if ( tokens
.CountTokens() != 3 ) {
470 // format is host:port,priority,Name
471 wxString addy
= tokens
.GetNextToken().Strip( wxString::both
);
472 wxString prio
= tokens
.GetNextToken().Strip( wxString::both
);
473 wxString name
= tokens
.GetNextToken().Strip( wxString::both
);
475 wxString host
= addy
.BeforeFirst( wxT(':') );
476 wxString port
= addy
.AfterFirst( wxT(':') );
479 int priority
= StrToLong( prio
);
480 if (priority
< SRV_PR_MIN
|| priority
> SRV_PR_MAX
) {
481 priority
= SRV_PR_NORMAL
;
485 // We need a valid name for the list
486 if ( name
.IsEmpty() ) {
491 // create server object and add it to the list
492 CServer
* server
= new CServer( StrToLong( port
), host
);
494 server
->SetListName( name
);
495 server
->SetIsStaticMember( true );
496 server
->SetPreference( priority
);
499 // Try to add the server to the list
500 if ( !theApp
->AddServer( server
) ) {
502 CServer
* existing
= GetServerByAddress( host
, StrToULong( port
) );
504 existing
->SetListName( name
);
505 existing
->SetIsStaticMember( true );
506 existing
->SetPreference( priority
);
508 Notify_ServerRefresh( existing
);
514 void CServerList::SaveStaticServers()
517 if (!file
.Open(m_staticServersConfig
, CTextFile::write
)) {
518 AddLogLineN(CFormat( _("Failed to open '%s'") ) % m_staticServersConfig
);
522 for (CInternalList::const_iterator it
= m_servers
.begin(); it
!= m_servers
.end(); ++it
) {
523 const CServer
* server
= *it
;
525 if (server
->IsStaticMember()) {
526 file
.WriteLine(CFormat(wxT("%s:%u,%u,%s"))
527 % server
->GetAddress() % server
->GetPort()
528 % server
->GetPreferences() % server
->GetListName());
535 struct ServerPriorityComparator
{
536 // Return true iff lhs should strictly appear earlier in the list than rhs.
537 // In this case, we want higher priority servers to appear earlier.
538 bool operator()(const CServer
* lhs
, const CServer
* rhs
) {
541 rhs
->GetPreferences() == SRV_PR_LOW
||
542 rhs
->GetPreferences() == SRV_PR_NORMAL
||
543 rhs
->GetPreferences() == SRV_PR_HIGH
545 switch (lhs
->GetPreferences()) {
549 return rhs
->GetPreferences() == SRV_PR_LOW
;
551 return rhs
->GetPreferences() != SRV_PR_HIGH
;
559 void CServerList::Sort()
561 m_servers
.sort(ServerPriorityComparator());
562 // Once the list has been sorted, it doesn't really make sense to continue
563 // traversing the new order from the old position. Plus, there's a bug in
564 // version of libstdc++ before gcc4 such that iterators that were equal to
565 // end() were left dangling.
566 m_serverpos
= m_servers
.begin();
567 m_statserverpos
= m_servers
.begin();
571 CServer
* CServerList::GetNextServer(bool bOnlyObfuscated
)
573 while (bOnlyObfuscated
&& (m_serverpos
!= m_servers
.end()) && !((*m_serverpos
)->SupportsObfuscationTCP() || (*m_serverpos
)->SupportsObfuscationUDP())) {
574 wxASSERT(*m_serverpos
!= NULL
);
578 if (m_serverpos
== m_servers
.end()) {
582 return *m_serverpos
++;
590 CServer
* CServerList::GetNextStatServer()
592 if (m_statserverpos
== m_servers
.end()) {
593 m_statserverpos
= m_servers
.begin();
596 if (m_statserverpos
== m_servers
.end()) {
599 wxASSERT(*m_statserverpos
!= NULL
);
600 return *m_statserverpos
++;
605 CServer
* CServerList::GetServerByAddress(const wxString
& address
, uint16 port
) const
607 for (CInternalList::const_iterator it
= m_servers
.begin(); it
!= m_servers
.end(); ++it
) {
608 CServer
* const s
= *it
;
609 if (port
== s
->GetPort() && s
->GetAddress() == address
) {
617 CServer
* CServerList::GetServerByIP(uint32 nIP
) const
619 for (CInternalList::const_iterator it
= m_servers
.begin(); it
!= m_servers
.end(); ++it
){
620 CServer
* const s
= *it
;
621 if (s
->GetIP() == nIP
)
628 CServer
* CServerList::GetServerByIPTCP(uint32 nIP
, uint16 nPort
) const
630 for (CInternalList::const_iterator it
= m_servers
.begin(); it
!= m_servers
.end(); ++it
){
631 CServer
* const s
= *it
;
632 if (s
->GetIP() == nIP
&& s
->GetPort() == nPort
)
639 CServer
* CServerList::GetServerByIPUDP(uint32 nIP
, uint16 nUDPPort
, bool bObfuscationPorts
) const
641 for (CInternalList::const_iterator it
= m_servers
.begin(); it
!= m_servers
.end(); ++it
){
642 CServer
* const s
=*it
;
643 if (s
->GetIP() == nIP
644 && (s
->GetPort() == nUDPPort
-4
645 || (bObfuscationPorts
&& s
->GetObfuscationPortUDP() == nUDPPort
)
646 || s
->GetPort() == nUDPPort
- 12))
653 CServer
* CServerList::GetServerByECID(uint32 ecid
) const
655 for (CInternalList::const_iterator it
= m_servers
.begin(); it
!= m_servers
.end(); ++it
){
656 CServer
* const s
= *it
;
657 if (s
->ECID() == ecid
) {
665 void CServerList::SetStaticServer(CServer
* server
, bool isStatic
)
667 server
->SetIsStaticMember(isStatic
);
668 Notify_ServerRefresh(server
);
673 void CServerList::SetServerPrio(CServer
* server
, uint32 prio
)
675 server
->SetPreference(prio
);
676 Notify_ServerRefresh(server
);
680 bool CServerList::SaveServerMet()
682 CPath curservermet
= CPath(theApp
->ConfigDir
+ wxT("server.met"));
684 CFile
servermet(curservermet
, CFile::write_safe
);
685 if (!servermet
.IsOpened()) {
686 AddLogLineN(_("Failed to save server.met!"));
691 servermet
.WriteUInt8(0xE0);
692 servermet
.WriteUInt32( m_servers
.size() );
694 for ( CInternalList::const_iterator it
= m_servers
.begin(); it
!= m_servers
.end(); ++it
) {
695 const CServer
* const server
= *it
;
697 uint16 tagcount
= 12;
698 if (!server
->GetListName().IsEmpty()) {
701 if (!server
->GetDynIP().IsEmpty()) {
704 if (!server
->GetDescription().IsEmpty()) {
707 if (server
->GetConnPort() != server
->GetPort()) {
711 // For unicoded name, description, and dynip
712 if ( !server
->GetListName().IsEmpty() ) {
715 if ( !server
->GetDynIP().IsEmpty() ) {
718 if ( !server
->GetDescription().IsEmpty() ) {
721 if (!server
->GetVersion().IsEmpty()) {
725 if (server
->GetServerKeyUDP(true)) {
729 if (server
->GetServerKeyUDPIP()) {
733 if (server
->GetObfuscationPortTCP()) {
737 if (server
->GetObfuscationPortUDP()) {
741 servermet
.WriteUInt32(server
->GetIP());
742 servermet
.WriteUInt16(server
->GetPort());
743 servermet
.WriteUInt32(tagcount
);
745 if ( !server
->GetListName().IsEmpty() ) {
746 // This is BOM to keep eMule compatibility
747 CTagString( ST_SERVERNAME
, server
->GetListName()).WriteTagToFile( &servermet
, utf8strOptBOM
);
748 CTagString( ST_SERVERNAME
, server
->GetListName()).WriteTagToFile( &servermet
);
751 if ( !server
->GetDynIP().IsEmpty() ) {
752 // This is BOM to keep eMule compatibility
753 CTagString( ST_DYNIP
, server
->GetDynIP()).WriteTagToFile( &servermet
, utf8strOptBOM
);
754 CTagString( ST_DYNIP
, server
->GetDynIP()).WriteTagToFile( &servermet
);
757 if ( !server
->GetDescription().IsEmpty() ) {
758 // This is BOM to keep eMule compatibility
759 CTagString( ST_DESCRIPTION
, server
->GetDescription()).WriteTagToFile( &servermet
, utf8strOptBOM
);
760 CTagString( ST_DESCRIPTION
, server
->GetDescription()).WriteTagToFile( &servermet
);
763 if ( server
->GetConnPort() != server
->GetPort() ) {
764 CTagString( ST_AUXPORTSLIST
, server
->GetAuxPortsList() ).WriteTagToFile( &servermet
);
767 CTagInt32( ST_FAIL
, server
->GetFailedCount() ).WriteTagToFile( &servermet
);
768 CTagInt32( ST_PREFERENCE
, server
->GetPreferences() ).WriteTagToFile( &servermet
);
769 CTagInt32( wxT("users"), server
->GetUsers() ).WriteTagToFile( &servermet
);
770 CTagInt32( wxT("files"), server
->GetFiles() ).WriteTagToFile( &servermet
);
771 CTagInt32( ST_PING
, server
->GetPing() ).WriteTagToFile( &servermet
);
772 CTagInt32( ST_LASTPING
, server
->GetLastPingedTime()).WriteTagToFile( &servermet
);
773 CTagInt32( ST_MAXUSERS
, server
->GetMaxUsers() ).WriteTagToFile( &servermet
);
774 CTagInt32( ST_SOFTFILES
, server
->GetSoftFiles() ).WriteTagToFile( &servermet
);
775 CTagInt32( ST_HARDFILES
, server
->GetHardFiles() ).WriteTagToFile( &servermet
);
776 if (!server
->GetVersion().IsEmpty()){
777 CTagString( ST_VERSION
, server
->GetVersion() ).WriteTagToFile( &servermet
, utf8strOptBOM
);
778 CTagString( ST_VERSION
, server
->GetVersion() ).WriteTagToFile( &servermet
);
780 CTagInt32( ST_UDPFLAGS
, server
->GetUDPFlags() ).WriteTagToFile( &servermet
);
781 CTagInt32( ST_LOWIDUSERS
, server
->GetLowIDUsers() ).WriteTagToFile( &servermet
);
783 if (server
->GetServerKeyUDP(true)) {
784 CTagInt32(ST_UDPKEY
, server
->GetServerKeyUDP(true)).WriteTagToFile( &servermet
);
787 if (server
->GetServerKeyUDPIP()) {
788 CTagInt32(ST_UDPKEYIP
, server
->GetServerKeyUDPIP()).WriteTagToFile( &servermet
);
791 if (server
->GetObfuscationPortTCP()) {
792 CTagInt16(ST_TCPPORTOBFUSCATION
, server
->GetObfuscationPortTCP()).WriteTagToFile( &servermet
);
795 if (server
->GetObfuscationPortUDP()) {
796 CTagInt16(ST_UDPPORTOBFUSCATION
, server
->GetObfuscationPortUDP()).WriteTagToFile( &servermet
);
800 // Now server.met.new is ready to be closed and renamed to server.met.
801 // But first rename existing server.met to server.met.bak (replacing old .bak file).
802 const CPath oldservermet
= CPath(theApp
->ConfigDir
+ wxT("server.met.bak"));
803 if (curservermet
.FileExists()) {
804 CPath::RenameFile(curservermet
, oldservermet
, true);
809 } catch (const CIOFailureException
& e
) {
810 AddLogLineC(wxT("IO failure while writing 'server.met': ") + e
.what());
818 void CServerList::RemoveDeadServers()
820 if ( thePrefs::DeadServer() ) {
821 for ( CInternalList::const_iterator it
= m_servers
.begin(); it
!= m_servers
.end(); ) {
822 CServer
* server
= *it
++;
823 if ( server
->GetFailedCount() > thePrefs::GetDeadserverRetries() && !server
->IsStaticMember()) {
824 RemoveServer(server
);
830 void CServerList::UpdateServerMetFromURL(const wxString
& strURL
)
832 if (strURL
.Find(wxT("://")) == -1) {
833 AddLogLineC(_("Invalid URL"));
836 m_URLUpdate
= strURL
;
837 wxString
strTempFilename(theApp
->ConfigDir
+ wxT("server.met.download"));
838 CHTTPDownloadThread
*downloader
= new CHTTPDownloadThread(strURL
, strTempFilename
, theApp
->ConfigDir
+ wxT("server.met"), HTTP_ServerMet
, false, false);
839 downloader
->Create();
844 void CServerList::DownloadFinished(uint32 result
)
846 if(result
== HTTP_Success
) {
847 const CPath tempFilename
= CPath(theApp
->ConfigDir
+ wxT("server.met.download"));
849 // curl succeeded. proceed with server.met loading
850 LoadServerMet(tempFilename
);
853 // So, file is loaded and merged, and also saved
854 CPath::RemoveFile(tempFilename
);
855 AddLogLineN(CFormat(_("Finished downloading the server list from %s")) % m_URLUpdate
);
856 // cppcheck-suppress duplicateBranch
857 } else if (result
== HTTP_Skipped
) {
858 AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % wxT("server.met"));
860 AddLogLineC(CFormat(_("Failed to download %s from %s")) % wxT("server.met") % m_URLUpdate
);
865 void CServerList::AutoUpdate()
868 uint8 url_count
= theApp
->glob_prefs
->adresses_list
.GetCount();
871 AddLogLineC(_("No server list address entry in 'addresses.dat' found. Please paste a valid server list address into this file in order to auto-update your server list"));
874 // Do current URL. Callback function will take care of the others.
875 while ( current_url_index
< url_count
) {
876 wxString URI
= theApp
->glob_prefs
->adresses_list
[current_url_index
];
877 // We use wxURL to validate the URI
878 if ( wxURL( URI
).GetError() == wxURL_NOERR
) {
879 // Ok, got a valid URI
881 wxString strTempFilename
=
882 theApp
->ConfigDir
+ wxT("server_auto.met");
884 _("Start downloading server list from %s")) % URI
);
885 CHTTPDownloadThread
*downloader
= new CHTTPDownloadThread(
886 URI
, strTempFilename
, theApp
->ConfigDir
+ wxT("server.met"), HTTP_ServerMetAuto
, false, false);
887 downloader
->Create();
893 _("WARNING: invalid URL specified for auto-updating of servers: %s") ) % URI
);
897 AddLogLineC(_("No valid server.met auto-download url on addresses.dat"));
901 void CServerList::AutoDownloadFinished(uint32 result
)
903 if (result
== HTTP_Success
) {
904 CPath tempFilename
= CPath(theApp
->ConfigDir
+ wxT("server_auto.met"));
906 // curl succeeded. proceed with server.met loading
907 LoadServerMet(tempFilename
);
910 // So, file is loaded and merged, and also saved
911 CPath::RemoveFile(tempFilename
);
913 AddLogLineC(CFormat(_("Failed to download the server list from %s") ) % m_URLUpdate
);
918 if (current_url_index
< theApp
->glob_prefs
->adresses_list
.GetCount()) {
925 void CServerList::ObserverAdded( ObserverType
* o
)
927 CObservableQueue
<CServer
*>::ObserverAdded( o
);
929 EventType::ValueList ilist
;
930 ilist
.reserve( m_servers
.size() );
931 ilist
.assign( m_servers
.begin(), m_servers
.end() );
933 NotifyObservers( EventType( EventType::INITIAL
, &ilist
), o
);
937 uint32
CServerList::GetAvgFile() const
939 //Since there is no real way to know how many files are in the kad network,
940 //I figure to try to use the ED2K network stats to find how many files the
941 //average user shares..
942 uint32 totaluser
= 0;
943 uint32 totalfile
= 0;
944 for (CInternalList::const_iterator it
= m_servers
.begin(); it
!= m_servers
.end(); ++it
){
945 const CServer
* const curr
= *it
;
946 //If this server has reported Users/Files and doesn't limit it's files too much
947 //use this in the calculation..
948 if( curr
->GetUsers() && curr
->GetFiles() && curr
->GetSoftFiles() > 1000 ) {
949 totaluser
+= curr
->GetUsers();
950 totalfile
+= curr
->GetFiles();
953 //If the user count is a little low, don't send back a average..
954 //I added 50 to the count as many servers do not allow a large amount of files to be shared..
955 //Therefore the estimate here will be lower then the actual.
956 //I would love to add a way for the client to send some statistics back so we could see the real
958 if ( totaluser
> 500000 ) {
959 return (totalfile
/totaluser
)+50;
966 std::vector
<const CServer
*> CServerList::CopySnapshot() const
968 std::vector
<const CServer
*> result
;
969 result
.reserve(m_servers
.size());
970 result
.assign(m_servers
.begin(), m_servers
.end());
975 void CServerList::FilterServers()
977 CInternalList::iterator it
= m_servers
.begin();
978 while (it
!= m_servers
.end()) {
979 CServer
* server
= *it
++;
981 if (server
->HasDynIP()) {
985 if (theApp
->ipfilter
->IsFiltered(server
->GetIP(), true)) {
986 if (server
== theApp
->serverconnect
->GetCurrentServer()) {
987 AddLogLineC(_("Local server is filtered by the IPFilters, reconnecting to a different server!"));
988 theApp
->serverconnect
->Disconnect();
989 RemoveServer(server
);
990 theApp
->serverconnect
->ConnectToAnyServer();
992 RemoveServer(server
);
998 void CServerList::CheckForExpiredUDPKeys() {
1000 if (!thePrefs::IsServerCryptLayerUDPEnabled()) {
1004 uint32 cKeysTotal
= 0;
1005 uint32 cKeysExpired
= 0;
1006 uint32 cPingDelayed
= 0;
1007 const uint32 dwIP
= theApp
->GetPublicIP();
1008 const uint32 tNow
= ::GetTickCount();
1009 wxASSERT( dwIP
!= 0 );
1011 for (CInternalList::const_iterator it
= m_servers
.begin(); it
!= m_servers
.end(); ++it
) {
1012 CServer
* pServer
= *it
;
1013 if (pServer
->SupportsObfuscationUDP() && pServer
->GetServerKeyUDP(true) != 0 && pServer
->GetServerKeyUDPIP() != dwIP
){
1016 if (tNow
- pServer
->GetRealLastPingedTime() < UDPSERVSTATMINREASKTIME
){
1018 // next ping: Now + (MinimumDelay - already elapsed time)
1019 pServer
->SetLastPingedTime((tNow
- (uint32
)UDPSERVSTATREASKTIME
) + (UDPSERVSTATMINREASKTIME
- (tNow
- pServer
->GetRealLastPingedTime())));
1021 pServer
->SetLastPingedTime(0);
1023 } else if (pServer
->SupportsObfuscationUDP() && pServer
->GetServerKeyUDP(false) != 0) {
1028 // File_checked_for_headers