Format string mayhem
[amule.git] / src / ServerList.cpp
blob280648cbc636fcfd0c0f916ca546a4719d71a752
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 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.
20 //
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
56 CServerList::CServerList()
58 m_serverpos = m_servers.end();
59 m_statserverpos = m_servers.end();
60 m_nLastED2KServerLinkCheck = ::GetTickCount();
64 bool CServerList::Init()
66 // Load Metfile
67 bool bRes = LoadServerMet(CPath(theApp->ConfigDir + wxT("server.met")));
69 // insert static servers from textfile
70 LoadStaticServers(theApp->ConfigDir + wxT("staticservers.dat"));
72 // Send the auto-update of server.met via HTTPThread requests
73 current_url_index = 0;
74 if ( thePrefs::AutoServerlist()) {
75 AutoUpdate();
78 return bRes;
82 bool CServerList::LoadServerMet(const CPath& path)
84 AddLogLineM(false, CFormat(_("Loading server.met file: %s")) % path);
86 bool merge = !m_servers.empty();
88 if (!path.FileExists()) {
89 AddLogLineM(false, _("Server.met file not found!"));
90 return false;
93 // Try to unpack the file, might be an archive
94 const wxChar* mets[] = { wxT("server.met"), NULL };
95 // Try to unpack the file, might be an archive
96 if (UnpackArchive(path, mets).second != EFT_Met) {
97 AddLogLineM(true, CFormat(_("Failed to load server.met file '%s', unknown format encountered.")) % path);
98 return false;
101 CFile servermet(path, CFile::read);
102 if ( !servermet.IsOpened() ){
103 AddLogLineM( false, _("Failed to open server.met!") );
104 return false;
108 try {
109 Notify_ServerFreeze();
111 byte version = servermet.ReadUInt8();
113 if (version != 0xE0 && version != MET_HEADER) {
114 AddLogLineC(CFormat(_("Server.met file corrupt, found invalid versiontag: 0x%x, size %i")) % version % sizeof(version));
115 Notify_ServerThaw();
116 return false;
119 uint32 fservercount = servermet.ReadUInt32();
121 ServerMet_Struct sbuffer;
122 uint32 iAddCount = 0;
124 for ( uint32 j = 0; j < fservercount; ++j ) {
125 sbuffer.ip = servermet.ReadUInt32();
126 sbuffer.port = servermet.ReadUInt16();
127 sbuffer.tagcount = servermet.ReadUInt32();
129 CServer* newserver = new CServer(&sbuffer);
131 // Load tags
132 for ( uint32 i = 0; i < sbuffer.tagcount; ++i ) {
133 newserver->AddTagFromFile(&servermet);
136 // Server priorities are not in sorted order
137 // High = 1, Low = 2, Normal = 0, so we have to check
138 // in a less logical fashion.
139 int priority = newserver->GetPreferences();
140 if (priority < SRV_PR_MIN || priority > SRV_PR_MAX) {
141 newserver->SetPreference(SRV_PR_NORMAL);
144 // set listname for server
145 if ( newserver->GetListName().IsEmpty() ) {
146 newserver->SetListName(wxT("Server ") +newserver->GetAddress());
150 if ( !theApp->AddServer(newserver) ) {
151 CServer* update = GetServerByAddress(newserver->GetAddress(), newserver->GetPort());
152 if(update) {
153 update->SetListName( newserver->GetListName());
154 if(!newserver->GetDescription().IsEmpty()) {
155 update->SetDescription( newserver->GetDescription());
157 Notify_ServerRefresh(update);
159 delete newserver;
160 } else {
161 ++iAddCount;
166 Notify_ServerThaw();
168 if (!merge) {
169 AddLogLineC(CFormat(wxPLURAL("%i server in server.met found", "%i servers in server.met found", fservercount)) % fservercount);
170 } else {
171 AddLogLineC(CFormat(wxPLURAL("%d server added", "%d servers added", iAddCount)) % iAddCount);
173 } catch (const CInvalidPacket& err) {
174 AddLogLineM(true, wxT("Error: the file server.met is corrupted: ") + err.what());
175 Notify_ServerThaw();
176 return false;
177 } catch (const CSafeIOException& err) {
178 AddLogLineM(true, wxT("IO error while reading 'server.met': ") + err.what());
179 Notify_ServerThaw();
180 return false;
183 return true;
187 bool CServerList::AddServer(CServer* in_server, bool fromUser)
189 if ( !in_server->GetPort() ) {
190 if ( fromUser ) {
191 AddLogLineM( true,
192 CFormat( _("Server not added: [%s:%d] does not specify a valid port.") )
193 % in_server->GetAddress()
194 % in_server->GetPort()
198 return false;
199 } else if (
200 !in_server->HasDynIP() &&
202 !IsGoodIP( in_server->GetIP(), thePrefs::FilterLanIPs() ) ||
203 ( // don't test for filtered while ipfilter is still loading,
204 // it will be filtered when filter loading is finished
205 theApp->ipfilter->IsReady()
206 && theApp->ipfilter->IsFiltered(in_server->GetIP(), true))
209 if ( fromUser ) {
210 AddLogLineM( true,
211 CFormat( _("Server not added: The IP of [%s:%d] is filtered or invalid.") )
212 % in_server->GetAddress()
213 % in_server->GetPort()
217 return false;
220 CServer* test_server = GetServerByAddress(in_server->GetAddress(), in_server->GetPort());
221 // Avoid duplicate (dynIP) servers: If the server which is to be added, is a dynIP-server
222 // but we don't know yet it's DN, we need to search for an already available server with
223 // that IP.
224 if (test_server == NULL && in_server->GetIP() != 0) {
225 test_server = GetServerByIPTCP(in_server->GetIP(), in_server->GetPort());
228 if (test_server) {
229 if ( fromUser ) {
230 AddLogLineM( true,
231 CFormat( _("Server not added: Server with matching IP:Port [%s:%d] found in list.") )
232 % in_server->GetAddress()
233 % in_server->GetPort()
237 test_server->ResetFailedCount();
238 Notify_ServerRefresh( test_server );
240 return false;
243 theStats::AddServer();
245 m_servers.push_back(in_server);
246 NotifyObservers( EventType( EventType::INSERTED, in_server ) );
248 if ( fromUser ) {
249 AddLogLineM( true,
250 CFormat( _("Server added: Server at [%s:%d] using the name '%s'.") )
251 % in_server->GetAddress()
252 % in_server->GetPort()
253 % in_server->GetListName()
258 return true;
262 void CServerList::ServerStats()
264 uint32 tNow = ::GetTickCount();
266 if (theApp->IsConnectedED2K() && m_servers.size() > 0) {
267 CServer* ping_server = GetNextStatServer();
268 CServer* test = ping_server;
269 if (!ping_server) {
270 return;
273 while (ping_server->GetLastPingedTime() && (tNow - ping_server->GetLastPingedTime()) < UDPSERVSTATREASKTIME) {
274 ping_server = GetNextStatServer();
275 if (ping_server == test) {
276 return;
280 if (ping_server->GetFailedCount() >= thePrefs::GetDeadserverRetries() && thePrefs::DeadServer() && !ping_server->IsStaticMember()) {
281 RemoveServer(ping_server);
282 return;
285 srand((unsigned)time(NULL));
286 ping_server->SetRealLastPingedTime(tNow); // this is not used to calcualte the next ping, but only to ensure a minimum delay for premature pings
287 if (!ping_server->GetCryptPingReplyPending() && (!ping_server->GetLastPingedTime() || (tNow - ping_server->GetLastPingedTime()) >= UDPSERVSTATREASKTIME) && theApp->GetPublicIP() && thePrefs::IsServerCryptLayerUDPEnabled()) {
288 // We try a obfsucation ping first and wait 20 seconds for an answer
289 // if it doesn't get responsed, we don't count it as error but continue with a normal ping
290 ping_server->SetCryptPingReplyPending(true);
291 uint32 nPacketLen = 4 + (uint8)(rand() % 16); // max padding 16 bytes
292 CScopedArray<byte> pRawPacket(nPacketLen);
293 uint32 dwChallenge = (rand() << 17) | (rand() << 2) | (rand() & 0x03);
294 if (dwChallenge == 0) {
295 dwChallenge++;
298 memcpy(pRawPacket.get(), &dwChallenge, sizeof(uint32));
299 for (uint32 i = 4; i < nPacketLen; i++) { // fillng up the remaining bytes with random data
300 pRawPacket[i] = (uint8)rand();
303 ping_server->SetChallenge(dwChallenge);
304 ping_server->SetLastPinged(tNow);
305 ping_server->SetLastPingedTime((tNow - (uint32)UDPSERVSTATREASKTIME) + 20); // give it 20 seconds to respond
307 AddDebugLogLineN(logServerUDP, CFormat(wxT(">> Sending OP__GlobServStatReq (obfuscated) to server %s:%u")) % ping_server->GetAddress() % ping_server->GetPort());
309 CPacket* packet = new CPacket(pRawPacket[1], nPacketLen - 2, pRawPacket[0]);
310 packet->CopyToDataBuffer(0, pRawPacket.get() + 2, nPacketLen - 2);
312 theStats::AddUpOverheadServer(packet->GetPacketSize());
313 theApp->serverconnect->SendUDPPacket(packet, ping_server, true, true /*raw packet*/, 12 /* Port offset is 12 for obfuscated encryption*/);
314 } else if (ping_server->GetCryptPingReplyPending() || theApp->GetPublicIP() == 0 || !thePrefs::IsServerCryptLayerUDPEnabled()){
315 // our obfsucation ping request was not answered, so probably the server doesn'T supports obfuscation
316 // continue with a normal request
317 if (ping_server->GetCryptPingReplyPending() && thePrefs::IsServerCryptLayerUDPEnabled()) {
318 AddDebugLogLineN(logServerUDP, wxT("CryptPing failed for server ") + ping_server->GetListName());
319 } else if (thePrefs::IsServerCryptLayerUDPEnabled()) {
320 AddDebugLogLineN(logServerUDP, wxT("CryptPing skipped because our public IP is unknown for server ") + ping_server->GetListName());
323 ping_server->SetCryptPingReplyPending(false);
325 CPacket* packet = new CPacket(OP_GLOBSERVSTATREQ, 4, OP_EDONKEYPROT);
326 uint32 challenge = 0x55AA0000 + (uint16)rand();
327 ping_server->SetChallenge(challenge);
328 packet->CopyUInt32ToDataBuffer(challenge);
329 ping_server->SetLastPinged(tNow);
330 ping_server->SetLastPingedTime(tNow - (rand() % HR2S(1)));
331 ping_server->AddFailedCount();
332 Notify_ServerRefresh(ping_server);
333 theStats::AddUpOverheadServer(packet->GetPacketSize());
334 theApp->serverconnect->SendUDPPacket(packet, ping_server, true);
335 } else {
336 wxFAIL;
342 void CServerList::RemoveServer(CServer* in_server)
344 if (in_server == theApp->serverconnect->GetCurrentServer()) {
345 theApp->ShowAlert(_("You are connected to the server you are trying to delete. please disconnect first."), _("Info"), wxOK);
346 } else {
347 CInternalList::iterator it = std::find(m_servers.begin(), m_servers.end(), in_server);
348 if ( it != m_servers.end() ) {
349 if (theApp->downloadqueue->GetUDPServer() == in_server) {
350 theApp->downloadqueue->SetUDPServer( 0 );
353 NotifyObservers( EventType( EventType::REMOVED, in_server ) );
355 if (m_serverpos == it) {
356 ++m_serverpos;
358 if (m_statserverpos == it) {
359 ++m_statserverpos;
361 m_servers.erase(it);
362 theStats::DeleteServer();
364 Notify_ServerRemove(in_server);
365 delete in_server;
371 void CServerList::RemoveAllServers()
373 NotifyObservers( EventType( EventType::CLEARED ) );
375 theStats::DeleteAllServers();
376 // no connection, safely remove all servers
377 while ( !m_servers.empty() ) {
378 delete m_servers.back();
379 m_servers.pop_back();
381 m_serverpos = m_servers.end();
382 m_statserverpos = m_servers.end();
386 void CServerList::GetStatus(uint32 &failed, uint32 &user, uint32 &file, uint32 &tuser, uint32 &tfile,float &occ)
388 failed = 0;
389 user = 0;
390 file = 0;
391 tuser=0;
392 tfile = 0;
393 occ=0;
394 uint32 maxusers=0;
395 uint32 tuserk = 0;
397 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
398 const CServer* const curr = *it;
399 if( curr->GetFailedCount() ) {
400 ++failed;
401 } else {
402 user += curr->GetUsers();
403 file += curr->GetFiles();
405 tuser += curr->GetUsers();
406 tfile += curr->GetFiles();
408 if (curr->GetMaxUsers()) {
409 tuserk += curr->GetUsers(); // total users on servers with known maximum
410 maxusers+=curr->GetMaxUsers();
413 if (maxusers>0) {
414 occ=(float)(tuserk*100)/maxusers;
419 void CServerList::GetUserFileStatus(uint32 &user, uint32 &file)
421 user = 0;
422 file = 0;
423 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
424 const CServer* const curr = *it;
425 if( !curr->GetFailedCount() ) {
426 user += curr->GetUsers();
427 file += curr->GetFiles();
433 CServerList::~CServerList()
435 if (thePrefs::GetNetworkED2K()) {
436 SaveServerMet();
438 while ( !m_servers.empty() ) {
439 delete m_servers.back();
440 m_servers.pop_back();
445 void CServerList::LoadStaticServers( const wxString& filename )
447 if ( !CPath::FileExists( filename ) ) {
448 return;
451 wxFileInputStream stream( filename );
452 wxTextInputStream f(stream);
454 while ( !stream.Eof() ) {
455 wxString line = f.ReadLine();
457 // Skip comments
458 if ( line.GetChar(0) == '#' || line.GetChar(0) == '/') {
459 continue;
462 wxStringTokenizer tokens( line, wxT(",") );
464 if ( tokens.CountTokens() != 3 ) {
465 continue;
469 // format is host:port,priority,Name
470 wxString addy = tokens.GetNextToken().Strip( wxString::both );
471 wxString prio = tokens.GetNextToken().Strip( wxString::both );
472 wxString name = tokens.GetNextToken().Strip( wxString::both );
474 wxString host = addy.BeforeFirst( wxT(':') );
475 wxString port = addy.AfterFirst( wxT(':') );
478 int priority = StrToLong( prio );
479 if (priority < SRV_PR_MIN || priority > SRV_PR_MAX) {
480 priority = SRV_PR_NORMAL;
484 // We need a valid name for the list
485 if ( name.IsEmpty() ) {
486 name = addy;
490 // create server object and add it to the list
491 CServer* server = new CServer( StrToLong( port ), host );
493 server->SetListName( name );
494 server->SetIsStaticMember( true );
495 server->SetPreference( priority );
498 // Try to add the server to the list
499 if ( !theApp->AddServer( server ) ) {
500 delete server;
501 CServer* existing = GetServerByAddress( host, StrToULong( port ) );
502 if ( existing) {
503 existing->SetListName( name );
504 existing->SetIsStaticMember( true );
505 existing->SetPreference( priority );
507 Notify_ServerRefresh( existing );
514 struct ServerPriorityComparator {
515 // Return true iff lhs should strictly appear earlier in the list than rhs.
516 // In this case, we want higher priority servers to appear earlier.
517 bool operator()(const CServer* lhs, const CServer* rhs) {
518 wxASSERT
520 rhs->GetPreferences() == SRV_PR_LOW ||
521 rhs->GetPreferences() == SRV_PR_NORMAL ||
522 rhs->GetPreferences() == SRV_PR_HIGH
524 switch (lhs->GetPreferences()) {
525 case SRV_PR_LOW:
526 return false;
527 case SRV_PR_NORMAL:
528 return rhs->GetPreferences() == SRV_PR_LOW;
529 case SRV_PR_HIGH:
530 return rhs->GetPreferences() != SRV_PR_HIGH;
531 default:
532 wxFAIL;
533 return false;
538 void CServerList::Sort()
540 m_servers.sort(ServerPriorityComparator());
541 // Once the list has been sorted, it doesn't really make sense to continue
542 // traversing the new order from the old position. Plus, there's a bug in
543 // version of libstdc++ before gcc4 such that iterators that were equal to
544 // end() were left dangling.
545 m_serverpos = m_servers.begin();
546 m_statserverpos = m_servers.begin();
550 CServer* CServerList::GetNextServer(bool bOnlyObfuscated)
552 while (bOnlyObfuscated && (m_serverpos != m_servers.end()) && !((*m_serverpos)->SupportsObfuscationTCP() || (*m_serverpos)->SupportsObfuscationUDP())) {
553 wxASSERT(*m_serverpos != NULL);
554 ++m_serverpos;
557 if (m_serverpos == m_servers.end()) {
558 return 0;
559 } else {
560 if (*m_serverpos) {
561 return *m_serverpos++;
562 } else {
563 return 0;
569 CServer* CServerList::GetNextStatServer()
571 if (m_statserverpos == m_servers.end()) {
572 m_statserverpos = m_servers.begin();
575 if (m_statserverpos == m_servers.end()) {
576 return 0;
577 } else {
578 wxASSERT(*m_statserverpos != NULL);
579 return *m_statserverpos++;
584 CServer* CServerList::GetServerByAddress(const wxString& address, uint16 port) const
586 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
587 CServer* const s = *it;
588 if (port == s->GetPort() && s->GetAddress() == address) {
589 return s;
592 return NULL;
596 CServer* CServerList::GetServerByIP(uint32 nIP) const
598 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it){
599 CServer* const s = *it;
600 if (s->GetIP() == nIP)
601 return s;
603 return NULL;
607 CServer* CServerList::GetServerByIPTCP(uint32 nIP, uint16 nPort) const
609 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it){
610 CServer* const s = *it;
611 if (s->GetIP() == nIP && s->GetPort() == nPort)
612 return s;
614 return NULL;
617 CServer* CServerList::GetServerByIPUDP(uint32 nIP, uint16 nUDPPort, bool bObfuscationPorts) 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 && (s->GetPort() == nUDPPort-4
623 || (bObfuscationPorts && s->GetObfuscationPortUDP() == nUDPPort)
624 || s->GetPort() == nUDPPort - 12))
625 return s;
627 return NULL;
630 bool CServerList::SaveServerMet()
632 CPath newservermet = CPath(theApp->ConfigDir + wxT("server.met.new"));
634 CFile servermet( newservermet, CFile::write );
635 if (!servermet.IsOpened()) {
636 AddLogLineM(false,_("Failed to save server.met!"));
637 return false;
641 try {
642 servermet.WriteUInt8(0xE0);
643 servermet.WriteUInt32( m_servers.size() );
645 for ( CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
646 const CServer* const server = *it;
648 uint16 tagcount = 12;
649 if (!server->GetListName().IsEmpty()) {
650 ++tagcount;
652 if (!server->GetDynIP().IsEmpty()) {
653 ++tagcount;
655 if (!server->GetDescription().IsEmpty()) {
656 ++tagcount;
658 if (server->GetConnPort() != server->GetPort()) {
659 ++tagcount;
662 // For unicoded name, description, and dynip
663 if ( !server->GetListName().IsEmpty() ) {
664 ++tagcount;
666 if ( !server->GetDynIP().IsEmpty() ) {
667 ++tagcount;
669 if ( !server->GetDescription().IsEmpty() ) {
670 ++tagcount;
672 if (!server->GetVersion().IsEmpty()) {
673 ++tagcount;
676 if (server->GetServerKeyUDP(true)) {
677 ++tagcount;
680 if (server->GetServerKeyUDPIP()) {
681 ++tagcount;
684 if (server->GetObfuscationPortTCP()) {
685 ++tagcount;
688 if (server->GetObfuscationPortUDP()) {
689 ++tagcount;
692 servermet.WriteUInt32(server->GetIP());
693 servermet.WriteUInt16(server->GetPort());
694 servermet.WriteUInt32(tagcount);
696 if ( !server->GetListName().IsEmpty() ) {
697 // This is BOM to keep eMule compatibility
698 CTagString( ST_SERVERNAME, server->GetListName()).WriteTagToFile( &servermet, utf8strOptBOM);
699 CTagString( ST_SERVERNAME, server->GetListName()).WriteTagToFile( &servermet );
702 if ( !server->GetDynIP().IsEmpty() ) {
703 // This is BOM to keep eMule compatibility
704 CTagString( ST_DYNIP, server->GetDynIP()).WriteTagToFile( &servermet, utf8strOptBOM );
705 CTagString( ST_DYNIP, server->GetDynIP()).WriteTagToFile( &servermet );
708 if ( !server->GetDescription().IsEmpty() ) {
709 // This is BOM to keep eMule compatibility
710 CTagString( ST_DESCRIPTION, server->GetDescription()).WriteTagToFile( &servermet, utf8strOptBOM );
711 CTagString( ST_DESCRIPTION, server->GetDescription()).WriteTagToFile( &servermet );
714 if ( server->GetConnPort() != server->GetPort() ) {
715 CTagString( ST_AUXPORTSLIST, server->GetAuxPortsList() ).WriteTagToFile( &servermet );
718 CTagInt32( ST_FAIL, server->GetFailedCount() ).WriteTagToFile( &servermet );
719 CTagInt32( ST_PREFERENCE, server->GetPreferences() ).WriteTagToFile( &servermet );
720 CTagInt32( wxT("users"), server->GetUsers() ).WriteTagToFile( &servermet );
721 CTagInt32( wxT("files"), server->GetFiles() ).WriteTagToFile( &servermet );
722 CTagInt32( ST_PING, server->GetPing() ).WriteTagToFile( &servermet );
723 CTagInt32( ST_LASTPING, server->GetLastPingedTime()).WriteTagToFile( &servermet );
724 CTagInt32( ST_MAXUSERS, server->GetMaxUsers() ).WriteTagToFile( &servermet );
725 CTagInt32( ST_SOFTFILES, server->GetSoftFiles() ).WriteTagToFile( &servermet );
726 CTagInt32( ST_HARDFILES, server->GetHardFiles() ).WriteTagToFile( &servermet );
727 if (!server->GetVersion().IsEmpty()){
728 CTagString( ST_VERSION, server->GetVersion() ).WriteTagToFile( &servermet, utf8strOptBOM );
729 CTagString( ST_VERSION, server->GetVersion() ).WriteTagToFile( &servermet );
731 CTagInt32( ST_UDPFLAGS, server->GetUDPFlags() ).WriteTagToFile( &servermet );
732 CTagInt32( ST_LOWIDUSERS, server->GetLowIDUsers() ).WriteTagToFile( &servermet );
734 if (server->GetServerKeyUDP(true)) {
735 CTagInt32(ST_UDPKEY, server->GetServerKeyUDP(true)).WriteTagToFile( &servermet );;
738 if (server->GetServerKeyUDPIP()) {
739 CTagInt32(ST_UDPKEYIP, server->GetServerKeyUDPIP()).WriteTagToFile( &servermet );;;
742 if (server->GetObfuscationPortTCP()) {
743 CTagInt16(ST_TCPPORTOBFUSCATION, server->GetObfuscationPortTCP()).WriteTagToFile( &servermet );;;
746 if (server->GetObfuscationPortUDP()) {
747 CTagInt16(ST_UDPPORTOBFUSCATION, server->GetObfuscationPortUDP()).WriteTagToFile( &servermet );;;
751 } catch (const CIOFailureException& e) {
752 AddLogLineM(true, wxT("IO failure while writing 'server.met': ") + e.what());
753 return false;
756 servermet.Close();
757 const CPath curservermet = CPath(theApp->ConfigDir + wxT("server.met"));
758 const CPath oldservermet = CPath(theApp->ConfigDir + wxT("server_met.old"));
760 if (oldservermet.FileExists()) {
761 CPath::RemoveFile(oldservermet);
764 if (curservermet.FileExists()) {
765 CPath::RenameFile(curservermet, oldservermet);
768 CPath::RenameFile(newservermet, curservermet);
770 return true;
774 void CServerList::RemoveDeadServers()
776 if ( thePrefs::DeadServer() ) {
777 for ( CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ) {
778 CServer* server = *it++;
779 if ( server->GetFailedCount() > thePrefs::GetDeadserverRetries() && !server->IsStaticMember()) {
780 RemoveServer(server);
786 void CServerList::UpdateServerMetFromURL(const wxString& strURL)
788 if (strURL.Find(wxT("://")) == -1) {
789 AddLogLineM(true, _("Invalid URL"));
790 return;
792 m_URLUpdate = strURL;
793 wxString strTempFilename(theApp->ConfigDir + wxT("server.met.download"));
794 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(strURL, strTempFilename, theApp->ConfigDir + wxT("server.met"), HTTP_ServerMet, false, false);
795 downloader->Create();
796 downloader->Run();
800 void CServerList::DownloadFinished(uint32 result)
802 if(result == HTTP_Success) {
803 const CPath tempFilename = CPath(theApp->ConfigDir + wxT("server.met.download"));
805 // curl succeeded. proceed with server.met loading
806 LoadServerMet(tempFilename);
807 SaveServerMet();
809 // So, file is loaded and merged, and also saved
810 CPath::RemoveFile(tempFilename);
811 AddLogLineN(CFormat(_("Finished downloading the server list from %s")) % m_URLUpdate);
812 } else if (result == HTTP_Skipped) {
813 AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % wxT("server.met"));
814 } else {
815 AddLogLineC(CFormat(_("Failed to download %s from %s")) % wxT("server.met") % m_URLUpdate);
820 void CServerList::AutoUpdate()
823 uint8 url_count = theApp->glob_prefs->adresses_list.GetCount();
825 if (!url_count) {
826 AddLogLineM(true, _("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"));
827 return;
829 // Do current URL. Callback function will take care of the others.
830 while ( current_url_index < url_count ) {
831 wxString URI = theApp->glob_prefs->adresses_list[current_url_index];
832 // We use wxURL to validate the URI
833 if ( wxURL( URI ).GetError() == wxURL_NOERR ) {
834 // Ok, got a valid URI
835 m_URLUpdate = URI;
836 wxString strTempFilename =
837 theApp->ConfigDir + wxT("server_auto.met");
838 AddLogLineM(true, CFormat(
839 _("Start downloading server list from %s")) % URI);
840 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(
841 URI, strTempFilename, theApp->ConfigDir + wxT("server.met"), HTTP_ServerMetAuto, false, false);
842 downloader->Create();
843 downloader->Run();
845 return;
846 } else {
847 AddLogLineM(true, CFormat(
848 _("WARNING: invalid URL specified for auto-updating of servers: %s") ) % URI);
850 current_url_index++;
852 AddLogLineM(true, _("No valid server.met auto-download url on addresses.dat"));
856 void CServerList::AutoDownloadFinished(uint32 result)
858 if (result == HTTP_Success) {
859 CPath tempFilename = CPath(theApp->ConfigDir + wxT("server_auto.met"));
861 // curl succeeded. proceed with server.met loading
862 LoadServerMet(tempFilename);
863 SaveServerMet();
865 // So, file is loaded and merged, and also saved
866 CPath::RemoveFile(tempFilename);
867 } else {
868 AddLogLineM(true, CFormat(_("Failed to download the server list from %s") ) % m_URLUpdate);
871 ++current_url_index;
873 if (current_url_index < theApp->glob_prefs->adresses_list.GetCount()) {
874 // Next!
875 AutoUpdate();
880 void CServerList::ObserverAdded( ObserverType* o )
882 CObservableQueue<CServer*>::ObserverAdded( o );
884 EventType::ValueList ilist;
885 ilist.reserve( m_servers.size() );
886 ilist.assign( m_servers.begin(), m_servers.end() );
888 NotifyObservers( EventType( EventType::INITIAL, &ilist ), o );
892 uint32 CServerList::GetAvgFile() const
894 //Since there is no real way to know how many files are in the kad network,
895 //I figure to try to use the ED2K network stats to find how many files the
896 //average user shares..
897 uint32 totaluser = 0;
898 uint32 totalfile = 0;
899 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it){
900 const CServer* const curr = *it;
901 //If this server has reported Users/Files and doesn't limit it's files too much
902 //use this in the calculation..
903 if( curr->GetUsers() && curr->GetFiles() && curr->GetSoftFiles() > 1000 ) {
904 totaluser += curr->GetUsers();
905 totalfile += curr->GetFiles();
908 //If the user count is a little low, don't send back a average..
909 //I added 50 to the count as many servers do not allow a large amount of files to be shared..
910 //Therefore the estimate here will be lower then the actual.
911 //I would love to add a way for the client to send some statistics back so we could see the real
912 //values here..
913 if ( totaluser > 500000 ) {
914 return (totalfile/totaluser)+50;
915 } else {
916 return 0;
921 std::vector<const CServer*> CServerList::CopySnapshot() const
923 std::vector<const CServer*> result;
924 result.reserve(m_servers.size());
925 result.assign(m_servers.begin(), m_servers.end());
926 return result;
930 void CServerList::FilterServers()
932 CInternalList::iterator it = m_servers.begin();
933 while (it != m_servers.end()) {
934 CServer* server = *it++;
936 if (server->HasDynIP()) {
937 continue;
940 if (theApp->ipfilter->IsFiltered(server->GetIP(), true)) {
941 if (server == theApp->serverconnect->GetCurrentServer()) {
942 AddLogLineM(true, _("Local server is filtered by the IPFilters, reconnecting to a different server!"));
943 theApp->serverconnect->Disconnect();
944 RemoveServer(server);
945 theApp->serverconnect->ConnectToAnyServer();
946 } else {
947 RemoveServer(server);
953 void CServerList::CheckForExpiredUDPKeys() {
955 if (!thePrefs::IsServerCryptLayerUDPEnabled()) {
956 return;
959 uint32 cKeysTotal = 0;
960 uint32 cKeysExpired = 0;
961 uint32 cPingDelayed = 0;
962 const uint32 dwIP = theApp->GetPublicIP();
963 const uint32 tNow = ::GetTickCount();
964 wxASSERT( dwIP != 0 );
966 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
967 CServer* pServer = *it;
968 if (pServer->SupportsObfuscationUDP() && pServer->GetServerKeyUDP(true) != 0 && pServer->GetServerKeyUDPIP() != dwIP){
969 cKeysTotal++;
970 cKeysExpired++;
971 if (tNow - pServer->GetRealLastPingedTime() < UDPSERVSTATMINREASKTIME){
972 cPingDelayed++;
973 // next ping: Now + (MinimumDelay - already elapsed time)
974 pServer->SetLastPingedTime((tNow - (uint32)UDPSERVSTATREASKTIME) + (UDPSERVSTATMINREASKTIME - (tNow - pServer->GetRealLastPingedTime())));
975 } else {
976 pServer->SetLastPingedTime(0);
978 } else if (pServer->SupportsObfuscationUDP() && pServer->GetServerKeyUDP(false) != 0) {
979 cKeysTotal++;
983 // File_checked_for_headers