Replace dynamically built filter list with a static one
[amule.git] / src / ServerList.cpp
blobf2e14e0bd894e542c02f49dfacc21b00d30f51a1
1 //
2 // This file is part of the aMule Project.
3 //
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 )
6 //
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
9 // respective authors.
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
26 #include <wx/wx.h>
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
50 #include "Logger.h"
51 #include "ScopedPtr.h"
52 #include <common/Format.h>
53 #include "IPFilter.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()
68 // Load Metfile
69 bool bRes = LoadServerMet(CPath(thePrefs::GetConfigDir() + wxT("server.met")));
71 // insert static servers from textfile
72 m_staticServersConfig = thePrefs::GetConfigDir() + wxT("staticservers.dat");
73 LoadStaticServers();
75 // Send the auto-update of server.met via HTTPThread requests
76 current_url_index = 0;
77 if ( thePrefs::AutoServerlist()) {
78 AutoUpdate();
81 m_initialized = true;
82 return bRes;
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!"));
94 return false;
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);
102 return false;
105 CFile servermet(path, CFile::read);
106 if ( !servermet.IsOpened() ){
107 AddLogLineN(_("Failed to open server.met!") );
108 return false;
112 try {
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));
119 Notify_ServerThaw();
120 return false;
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);
135 // Load tags
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());
156 if(update) {
157 update->SetListName( newserver->GetListName());
158 if(!newserver->GetDescription().IsEmpty()) {
159 update->SetDescription( newserver->GetDescription());
161 Notify_ServerRefresh(update);
163 delete newserver;
164 } else {
165 ++iAddCount;
170 Notify_ServerThaw();
172 // cppcheck-suppress duplicateBranch
173 if (!merge) {
174 AddLogLineC(CFormat(wxPLURAL("%i server in server.met found", "%i servers in server.met found", fservercount)) % fservercount);
175 } else {
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());
180 Notify_ServerThaw();
181 return false;
182 } catch (const CSafeIOException& err) {
183 AddLogLineC(_("IO error while reading 'server.met': ") + err.what());
184 Notify_ServerThaw();
185 return false;
188 return true;
192 bool CServerList::AddServer(CServer* in_server, bool fromUser)
194 if ( !in_server->GetPort() ) {
195 if ( fromUser ) {
196 AddLogLineC(CFormat( _("Server not added: [%s:%d] does not specify a valid port.") )
197 % in_server->GetAddress()
198 % in_server->GetPort()
202 return false;
203 } else if (
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))
213 if ( fromUser ) {
214 AddLogLineC(CFormat( _("Server not added: The IP of [%s:%d] is filtered or invalid.") )
215 % in_server->GetAddress()
216 % in_server->GetPort()
220 return false;
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
226 // that IP.
227 if (test_server == NULL && in_server->GetIP() != 0) {
228 test_server = GetServerByIPTCP(in_server->GetIP(), in_server->GetPort());
231 if (test_server) {
232 if ( fromUser ) {
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 );
242 return false;
245 theStats::AddServer();
247 m_servers.push_back(in_server);
248 NotifyObservers( EventType( EventType::INSERTED, in_server ) );
250 if ( fromUser ) {
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()
259 return true;
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;
270 if (!ping_server) {
271 return;
274 while (ping_server->GetLastPingedTime() && (tNow - ping_server->GetLastPingedTime()) < UDPSERVSTATREASKTIME) {
275 ping_server = GetNextStatServer();
276 if (ping_server == test) {
277 return;
281 if (ping_server->GetFailedCount() >= thePrefs::GetDeadserverRetries() && thePrefs::DeadServer() && !ping_server->IsStaticMember()) {
282 RemoveServer(ping_server);
283 return;
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) {
296 dwChallenge++;
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);
336 } else {
337 wxFAIL;
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);
347 } else {
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) {
357 ++m_serverpos;
359 if (m_statserverpos == it) {
360 ++m_statserverpos;
362 m_servers.erase(it);
363 theStats::DeleteServer();
365 Notify_ServerRemove(in_server);
366 delete 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)
389 failed = 0;
390 user = 0;
391 file = 0;
392 tuser=0;
393 tfile = 0;
394 occ=0;
395 uint32 maxusers=0;
396 uint32 tuserk = 0;
398 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
399 const CServer* const curr = *it;
400 if( curr->GetFailedCount() ) {
401 ++failed;
402 } else {
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();
414 if (maxusers>0) {
415 occ=(float)(tuserk*100)/maxusers;
420 void CServerList::GetUserFileStatus(uint32 &user, uint32 &file)
422 user = 0;
423 file = 0;
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()) {
437 SaveServerMet();
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) ) {
449 return;
452 wxFileInputStream stream(m_staticServersConfig);
453 wxTextInputStream f(stream);
455 while ( !stream.Eof() ) {
456 wxString line = f.ReadLine();
458 // Skip comments
459 if (line.IsEmpty() || line.GetChar(0) == '#' || line.GetChar(0) == '/') {
460 continue;
463 wxStringTokenizer tokens( line, wxT(",") );
465 if ( tokens.CountTokens() != 3 ) {
466 continue;
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() ) {
487 name = addy;
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 ) ) {
501 delete server;
502 CServer* existing = GetServerByAddress( host, StrToULong( port ) );
503 if ( existing) {
504 existing->SetListName( name );
505 existing->SetIsStaticMember( true );
506 existing->SetPreference( priority );
508 Notify_ServerRefresh( existing );
514 void CServerList::SaveStaticServers()
516 CTextFile file;
517 if (!file.Open(m_staticServersConfig, CTextFile::write)) {
518 AddLogLineN(CFormat( _("Failed to open '%s'") ) % m_staticServersConfig );
519 return;
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());
532 file.Close();
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) {
539 wxASSERT
541 rhs->GetPreferences() == SRV_PR_LOW ||
542 rhs->GetPreferences() == SRV_PR_NORMAL ||
543 rhs->GetPreferences() == SRV_PR_HIGH
545 switch (lhs->GetPreferences()) {
546 case SRV_PR_LOW:
547 return false;
548 case SRV_PR_NORMAL:
549 return rhs->GetPreferences() == SRV_PR_LOW;
550 case SRV_PR_HIGH:
551 return rhs->GetPreferences() != SRV_PR_HIGH;
552 default:
553 wxFAIL;
554 return false;
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);
575 ++m_serverpos;
578 if (m_serverpos == m_servers.end()) {
579 return 0;
580 } else {
581 if (*m_serverpos) {
582 return *m_serverpos++;
583 } else {
584 return 0;
590 CServer* CServerList::GetNextStatServer()
592 if (m_statserverpos == m_servers.end()) {
593 m_statserverpos = m_servers.begin();
596 if (m_statserverpos == m_servers.end()) {
597 return 0;
598 } else {
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) {
610 return s;
613 return NULL;
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)
622 return s;
624 return NULL;
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)
633 return s;
635 return NULL;
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))
647 return s;
649 return NULL;
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) {
658 return s;
661 return NULL;
665 void CServerList::SetStaticServer(CServer* server, bool isStatic)
667 server->SetIsStaticMember(isStatic);
668 Notify_ServerRefresh(server);
669 SaveStaticServers();
673 void CServerList::SetServerPrio(CServer* server, uint32 prio)
675 server->SetPreference(prio);
676 Notify_ServerRefresh(server);
680 bool CServerList::SaveServerMet()
682 CPath curservermet = CPath(thePrefs::GetConfigDir() + wxT("server.met"));
684 CFile servermet(curservermet, CFile::write_safe);
685 if (!servermet.IsOpened()) {
686 AddLogLineN(_("Failed to save server.met!"));
687 return false;
690 try {
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()) {
699 ++tagcount;
701 if (!server->GetDynIP().IsEmpty()) {
702 ++tagcount;
704 if (!server->GetDescription().IsEmpty()) {
705 ++tagcount;
707 if (server->GetConnPort() != server->GetPort()) {
708 ++tagcount;
711 // For unicoded name, description, and dynip
712 if ( !server->GetListName().IsEmpty() ) {
713 ++tagcount;
715 if ( !server->GetDynIP().IsEmpty() ) {
716 ++tagcount;
718 if ( !server->GetDescription().IsEmpty() ) {
719 ++tagcount;
721 if (!server->GetVersion().IsEmpty()) {
722 ++tagcount;
725 if (server->GetServerKeyUDP(true)) {
726 ++tagcount;
729 if (server->GetServerKeyUDPIP()) {
730 ++tagcount;
733 if (server->GetObfuscationPortTCP()) {
734 ++tagcount;
737 if (server->GetObfuscationPortUDP()) {
738 ++tagcount;
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(thePrefs::GetConfigDir() + wxT("server.met.bak"));
803 if (curservermet.FileExists()) {
804 CPath::RenameFile(curservermet, oldservermet, true);
807 servermet.Close();
809 } catch (const CIOFailureException& e) {
810 AddLogLineC(wxT("IO failure while writing 'server.met': ") + e.what());
811 return false;
814 return true;
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"));
834 return;
836 m_URLUpdate = strURL;
837 wxString strTempFilename(thePrefs::GetConfigDir() + wxT("server.met.download"));
838 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(strURL, strTempFilename, thePrefs::GetConfigDir() + wxT("server.met"), HTTP_ServerMet, false, false);
839 downloader->Create();
840 downloader->Run();
844 bool CServerList::DownloadFinished(uint32 result)
846 bool ret = false;
847 if(result == HTTP_Success) {
848 const CPath tempFilename = CPath(thePrefs::GetConfigDir() + wxT("server.met.download"));
850 // curl succeeded. proceed with server.met loading
851 LoadServerMet(tempFilename);
852 SaveServerMet();
854 // So, file is loaded and merged, and also saved
855 CPath::RemoveFile(tempFilename);
856 AddLogLineN(CFormat(_("Finished downloading the server list from %s")) % m_URLUpdate);
857 ret = true;
858 // cppcheck-suppress duplicateBranch
859 } else if (result == HTTP_Skipped) {
860 AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % wxT("server.met"));
861 } else {
862 AddLogLineC(CFormat(_("Failed to download %s from %s")) % wxT("server.met") % m_URLUpdate);
864 return ret;
868 void CServerList::AutoUpdate()
871 uint8 url_count = theApp->glob_prefs->adresses_list.GetCount();
873 if (!url_count) {
874 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"));
875 return;
877 // Do current URL. Callback function will take care of the others.
878 while ( current_url_index < url_count ) {
879 wxString URI = theApp->glob_prefs->adresses_list[current_url_index];
880 // We use wxURL to validate the URI
881 if ( wxURL( URI ).GetError() == wxURL_NOERR ) {
882 // Ok, got a valid URI
883 m_URLUpdate = URI;
884 wxString strTempFilename =
885 thePrefs::GetConfigDir() + wxT("server_auto.met");
886 AddLogLineC(CFormat(
887 _("Start downloading server list from %s")) % URI);
888 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(
889 URI, strTempFilename, thePrefs::GetConfigDir() + wxT("server.met"), HTTP_ServerMetAuto, false, false);
890 downloader->Create();
891 downloader->Run();
893 return;
894 } else {
895 AddLogLineC(CFormat(
896 _("WARNING: invalid URL specified for auto-updating of servers: %s") ) % URI);
898 current_url_index++;
900 AddLogLineC(_("No valid server.met auto-download url on addresses.dat"));
904 void CServerList::AutoDownloadFinished(uint32 result)
906 if (result == HTTP_Success) {
907 CPath tempFilename = CPath(thePrefs::GetConfigDir() + wxT("server_auto.met"));
909 // curl succeeded. proceed with server.met loading
910 LoadServerMet(tempFilename);
911 SaveServerMet();
913 // So, file is loaded and merged, and also saved
914 CPath::RemoveFile(tempFilename);
915 } else {
916 AddLogLineC(CFormat(_("Failed to download the server list from %s") ) % m_URLUpdate);
919 ++current_url_index;
921 if (current_url_index < theApp->glob_prefs->adresses_list.GetCount()) {
922 // Next!
923 AutoUpdate();
928 void CServerList::ObserverAdded( ObserverType* o )
930 CObservableQueue<CServer*>::ObserverAdded( o );
932 EventType::ValueList ilist;
933 ilist.reserve( m_servers.size() );
934 ilist.assign( m_servers.begin(), m_servers.end() );
936 NotifyObservers( EventType( EventType::INITIAL, &ilist ), o );
940 uint32 CServerList::GetAvgFile() const
942 //Since there is no real way to know how many files are in the kad network,
943 //I figure to try to use the ED2K network stats to find how many files the
944 //average user shares..
945 uint32 totaluser = 0;
946 uint32 totalfile = 0;
947 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it){
948 const CServer* const curr = *it;
949 //If this server has reported Users/Files and doesn't limit it's files too much
950 //use this in the calculation..
951 if( curr->GetUsers() && curr->GetFiles() && curr->GetSoftFiles() > 1000 ) {
952 totaluser += curr->GetUsers();
953 totalfile += curr->GetFiles();
956 //If the user count is a little low, don't send back a average..
957 //I added 50 to the count as many servers do not allow a large amount of files to be shared..
958 //Therefore the estimate here will be lower then the actual.
959 //I would love to add a way for the client to send some statistics back so we could see the real
960 //values here..
961 if ( totaluser > 500000 ) {
962 return (totalfile/totaluser)+50;
963 } else {
964 return 0;
969 std::vector<const CServer*> CServerList::CopySnapshot() const
971 std::vector<const CServer*> result;
972 result.reserve(m_servers.size());
973 result.assign(m_servers.begin(), m_servers.end());
974 return result;
978 void CServerList::FilterServers()
980 CInternalList::iterator it = m_servers.begin();
981 while (it != m_servers.end()) {
982 CServer* server = *it++;
984 if (server->HasDynIP()) {
985 continue;
988 if (theApp->ipfilter->IsFiltered(server->GetIP(), true)) {
989 if (server == theApp->serverconnect->GetCurrentServer()) {
990 AddLogLineC(_("Local server is filtered by the IPFilters, reconnecting to a different server!"));
991 theApp->serverconnect->Disconnect();
992 RemoveServer(server);
993 theApp->serverconnect->ConnectToAnyServer();
994 } else {
995 RemoveServer(server);
1001 void CServerList::CheckForExpiredUDPKeys() {
1003 if (!thePrefs::IsServerCryptLayerUDPEnabled()) {
1004 return;
1007 uint32 cKeysTotal = 0;
1008 uint32 cKeysExpired = 0;
1009 uint32 cPingDelayed = 0;
1010 const uint32 dwIP = theApp->GetPublicIP();
1011 const uint32 tNow = ::GetTickCount();
1012 wxASSERT( dwIP != 0 );
1014 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
1015 CServer* pServer = *it;
1016 if (pServer->SupportsObfuscationUDP() && pServer->GetServerKeyUDP(true) != 0 && pServer->GetServerKeyUDPIP() != dwIP){
1017 cKeysTotal++;
1018 cKeysExpired++;
1019 if (tNow - pServer->GetRealLastPingedTime() < UDPSERVSTATMINREASKTIME){
1020 cPingDelayed++;
1021 // next ping: Now + (MinimumDelay - already elapsed time)
1022 pServer->SetLastPingedTime((tNow - (uint32)UDPSERVSTATREASKTIME) + (UDPSERVSTATMINREASKTIME - (tNow - pServer->GetRealLastPingedTime())));
1023 } else {
1024 pServer->SetLastPingedTime(0);
1026 } else if (pServer->SupportsObfuscationUDP() && pServer->GetServerKeyUDP(false) != 0) {
1027 cKeysTotal++;
1031 // File_checked_for_headers