Upstream tarball 20080512
[amule.git] / src / ServerList.cpp
blob4cd923cab2062406f7a2923b333c9c30535d0f6a
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 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 <common/Format.h>
52 #include "IPFilter.h"
53 #include <common/FileFunctions.h> // Needed for UnpackArchive
55 CServerList::CServerList()
57 m_serverpos = m_servers.end();
58 m_statserverpos = m_servers.end();
59 m_nLastED2KServerLinkCheck = ::GetTickCount();
63 bool CServerList::Init()
65 // Load Metfile
66 bool bRes = LoadServerMet(CPath(theApp->ConfigDir + wxT("server.met")));
68 // insert static servers from textfile
69 LoadStaticServers(theApp->ConfigDir + wxT("staticservers.dat"));
71 // Send the auto-update of server.met via HTTPThread requests
72 current_url_index = 0;
73 if ( thePrefs::AutoServerlist()) {
74 AutoUpdate();
77 return bRes;
81 bool CServerList::LoadServerMet(const CPath& path)
83 AddLogLineM(false, CFormat(_("Loading server.met file: %s")) % path);
85 bool merge = !m_servers.empty();
87 if (!path.FileExists()) {
88 AddLogLineM(false, _("Server.met file not found!"));
89 return false;
92 // Try to unpack the file, might be an archive
93 const wxChar* mets[] = { wxT("server.met"), NULL };
94 // Try to unpack the file, might be an archive
95 if (UnpackArchive(path, mets).second != EFT_Met) {
96 AddLogLineM(true, CFormat(_("Failed to load server.met file '%s', unknown format encountered.")) % path);
97 return false;
100 CFile servermet(path, CFile::read);
101 if ( !servermet.IsOpened() ){
102 AddLogLineM( false, _("Failed to open server.met!") );
103 return false;
107 try {
108 Notify_ServerFreeze();
110 byte version = servermet.ReadUInt8();
112 if (version != 0xE0 && version != MET_HEADER) {
113 AddLogLineM(true, wxString::Format(_("Server.met file corrupt, found invalid versiontag: 0x%x, size %i"), version, sizeof(version)));
114 Notify_ServerThaw();
115 return false;
118 uint32 fservercount = servermet.ReadUInt32();
120 ServerMet_Struct sbuffer;
121 uint32 iAddCount = 0;
123 for ( uint32 j = 0; j < fservercount; ++j ) {
124 sbuffer.ip = servermet.ReadUInt32();
125 sbuffer.port = servermet.ReadUInt16();
126 sbuffer.tagcount = servermet.ReadUInt32();
128 CServer* newserver = new CServer(&sbuffer);
130 // Load tags
131 for ( uint32 i = 0; i < sbuffer.tagcount; ++i ) {
132 newserver->AddTagFromFile(&servermet);
135 // Server priorities are not in sorted order
136 // High = 1, Low = 2, Normal = 0, so we have to check
137 // in a less logical fashion.
138 int priority = newserver->GetPreferences();
139 if (priority < SRV_PR_MIN || priority > SRV_PR_MAX) {
140 newserver->SetPreference(SRV_PR_NORMAL);
143 // set listname for server
144 if ( newserver->GetListName().IsEmpty() ) {
145 newserver->SetListName(wxT("Server ") +newserver->GetAddress());
149 if ( !theApp->AddServer(newserver) ) {
150 CServer* update = GetServerByAddress(newserver->GetAddress(), newserver->GetPort());
151 if(update) {
152 update->SetListName( newserver->GetListName());
153 if(!newserver->GetDescription().IsEmpty()) {
154 update->SetDescription( newserver->GetDescription());
156 Notify_ServerRefresh(update);
158 delete newserver;
159 } else {
160 ++iAddCount;
165 Notify_ServerThaw();
167 if (!merge) {
168 AddLogLineM(true, wxString::Format(wxPLURAL("%i server in server.met found", "%i servers in server.met found", fservercount), fservercount));
169 } else {
170 AddLogLineM(true, wxString::Format(wxPLURAL("%d server added", "%d servers added", iAddCount), iAddCount));
172 } catch (const CInvalidPacket& err) {
173 AddLogLineM(true, wxT("Error: the file server.met is corrupted: ") + err.what());
174 Notify_ServerThaw();
175 return false;
176 } catch (const CSafeIOException& err) {
177 AddLogLineM(true, wxT("IO error while reading 'server.met': ") + err.what());
178 Notify_ServerThaw();
179 return false;
182 return true;
186 bool CServerList::AddServer(CServer* in_server, bool fromUser)
188 if ( !in_server->GetPort() ) {
189 if ( fromUser ) {
190 AddLogLineM( true,
191 CFormat( _("Server not added: [%s:%d] does not specify a valid port.") )
192 % in_server->GetAddress()
193 % in_server->GetPort()
197 return false;
198 } else if (
199 !in_server->HasDynIP() &&
201 !IsGoodIP( in_server->GetIP(), thePrefs::FilterLanIPs() ) ||
202 theApp->ipfilter->IsFiltered( in_server->GetIP(), true )
205 if ( fromUser ) {
206 AddLogLineM( true,
207 CFormat( _("Server not added: The IP of [%s:%d] is filtered or invalid.") )
208 % in_server->GetAddress()
209 % in_server->GetPort()
213 return false;
216 CServer* test_server = GetServerByAddress(in_server->GetAddress(), in_server->GetPort());
217 // Avoid duplicate (dynIP) servers: If the server which is to be added, is a dynIP-server
218 // but we don't know yet it's DN, we need to search for an already available server with
219 // that IP.
220 if (test_server == NULL && in_server->GetIP() != 0) {
221 test_server = GetServerByIPTCP(in_server->GetIP(), in_server->GetPort());
224 if (test_server) {
225 if ( fromUser ) {
226 AddLogLineM( true,
227 CFormat( _("Server not added: Server with matching IP:Port [%s:%d] found in list.") )
228 % in_server->GetAddress()
229 % in_server->GetPort()
233 test_server->ResetFailedCount();
234 Notify_ServerRefresh( test_server );
236 return false;
239 theStats::AddServer();
241 m_servers.push_back(in_server);
242 NotifyObservers( EventType( EventType::INSERTED, in_server ) );
244 if ( fromUser ) {
245 AddLogLineM( true,
246 CFormat( _("Server added: Server at [%s:%d] using the name '%s'.") )
247 % in_server->GetAddress()
248 % in_server->GetPort()
249 % in_server->GetListName()
254 return true;
258 void CServerList::ServerStats()
260 uint32 tNow = ::GetTickCount();
262 if (theApp->IsConnectedED2K() && m_servers.size() > 0) {
263 CServer* ping_server = GetNextStatServer();
264 CServer* test = ping_server;
265 if (!ping_server) {
266 return;
269 while (ping_server->GetLastPingedTime() && (tNow - ping_server->GetLastPingedTime()) < UDPSERVSTATREASKTIME) {
270 ping_server = GetNextStatServer();
271 if (ping_server == test) {
272 return;
276 if (ping_server->GetFailedCount() >= thePrefs::GetDeadserverRetries() && thePrefs::DeadServer() && !ping_server->IsStaticMember()) {
277 RemoveServer(ping_server);
278 return;
281 srand((unsigned)time(NULL));
282 ping_server->SetRealLastPingedTime(tNow); // this is not used to calcualte the next ping, but only to ensure a minimum delay for premature pings
283 if (!ping_server->GetCryptPingReplyPending() && (!ping_server->GetLastPingedTime() || (tNow - ping_server->GetLastPingedTime()) >= UDPSERVSTATREASKTIME) && theApp->GetPublicIP() && thePrefs::IsServerCryptLayerUDPEnabled()) {
284 // We try a obfsucation ping first and wait 20 seconds for an answer
285 // if it doesn't get responsed, we don't count it as error but continue with a normal ping
286 ping_server->SetCryptPingReplyPending(true);
287 uint32 nPacketLen = 4 + (uint8)(rand() % 16); // max padding 16 bytes
288 byte* pRawPacket = new byte[nPacketLen];
289 uint32 dwChallenge = (rand() << 17) | (rand() << 2) | (rand() & 0x03);
290 if (dwChallenge == 0) {
291 dwChallenge++;
294 memcpy(pRawPacket, &dwChallenge, sizeof(uint32));
295 for (uint32 i = 4; i < nPacketLen; i++) { // fillng up the remaining bytes with random data
296 pRawPacket[i] = (uint8)rand();
299 ping_server->SetChallenge(dwChallenge);
300 ping_server->SetLastPinged(tNow);
301 ping_server->SetLastPingedTime((tNow - (uint32)UDPSERVSTATREASKTIME) + 20); // give it 20 seconds to respond
303 AddDebugLogLineM(false, logServerUDP, CFormat(wxT(">> Sending OP__GlobServStatReq (obfuscated) to server %s:%u")) % ping_server->GetAddress() % ping_server->GetPort());
305 CPacket* packet = new CPacket(pRawPacket[1], nPacketLen - 2, pRawPacket[0]);
306 packet->CopyToDataBuffer(0, pRawPacket + 2, nPacketLen - 2);
308 theStats::AddUpOverheadServer(packet->GetPacketSize());
309 theApp->serverconnect->SendUDPPacket(packet, ping_server, true, true /*raw packet*/, 12 /* Port offset is 12 for obfuscated encryption*/);
310 } else if (ping_server->GetCryptPingReplyPending() || theApp->GetPublicIP() == 0 || !thePrefs::IsServerCryptLayerUDPEnabled()){
311 // our obfsucation ping request was not answered, so probably the server doesn'T supports obfuscation
312 // continue with a normal request
313 if (ping_server->GetCryptPingReplyPending() && thePrefs::IsServerCryptLayerUDPEnabled()) {
314 AddDebugLogLineM(false, logServerUDP, wxT("CryptPing failed for server ") + ping_server->GetListName());
315 } else if (thePrefs::IsServerCryptLayerUDPEnabled()) {
316 AddDebugLogLineM(false, logServerUDP, wxT("CryptPing skipped because our public IP is unknown for server ") + ping_server->GetListName());
319 ping_server->SetCryptPingReplyPending(false);
321 CPacket* packet = new CPacket(OP_GLOBSERVSTATREQ, 4, OP_EDONKEYPROT);
322 uint32 challenge = 0x55AA0000 + (uint16)rand();
323 ping_server->SetChallenge(challenge);
324 packet->CopyUInt32ToDataBuffer(challenge);
325 ping_server->SetLastPinged(tNow);
326 ping_server->SetLastPingedTime(tNow - (rand() % HR2S(1)));
327 ping_server->AddFailedCount();
328 Notify_ServerRefresh(ping_server);
329 theStats::AddUpOverheadServer(packet->GetPacketSize());
330 theApp->serverconnect->SendUDPPacket(packet, ping_server, true);
331 } else {
332 wxASSERT( false );
338 void CServerList::RemoveServer(CServer* in_server)
340 if (in_server == theApp->serverconnect->GetCurrentServer()) {
341 theApp->ShowAlert(_("You are connected to the server you are trying to delete. please disconnect first."), _("Info"), wxOK);
342 } else {
343 CInternalList::iterator it = std::find(m_servers.begin(), m_servers.end(), in_server);
344 if ( it != m_servers.end() ) {
345 if (theApp->downloadqueue->GetUDPServer() == in_server) {
346 theApp->downloadqueue->SetUDPServer( 0 );
349 NotifyObservers( EventType( EventType::REMOVED, in_server ) );
351 if (m_serverpos == it) {
352 ++m_serverpos;
354 if (m_statserverpos == it) {
355 ++m_statserverpos;
357 m_servers.erase(it);
358 theStats::DeleteServer();
360 Notify_ServerRemove(in_server);
361 delete in_server;
367 void CServerList::RemoveAllServers()
369 NotifyObservers( EventType( EventType::CLEARED ) );
371 theStats::DeleteAllServers();
372 // no connection, safely remove all servers
373 while ( !m_servers.empty() ) {
374 delete m_servers.back();
375 m_servers.pop_back();
377 m_serverpos = m_servers.end();
378 m_statserverpos = m_servers.end();
382 void CServerList::GetStatus(uint32 &failed, uint32 &user, uint32 &file, uint32 &tuser, uint32 &tfile,float &occ)
384 failed = 0;
385 user = 0;
386 file = 0;
387 tuser=0;
388 tfile = 0;
389 occ=0;
390 uint32 maxusers=0;
391 uint32 tuserk = 0;
393 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
394 const CServer* const curr = *it;
395 if( curr->GetFailedCount() ) {
396 ++failed;
397 } else {
398 user += curr->GetUsers();
399 file += curr->GetFiles();
401 tuser += curr->GetUsers();
402 tfile += curr->GetFiles();
404 if (curr->GetMaxUsers()) {
405 tuserk += curr->GetUsers(); // total users on servers with known maximum
406 maxusers+=curr->GetMaxUsers();
409 if (maxusers>0) {
410 occ=(float)(tuserk*100)/maxusers;
415 void CServerList::GetUserFileStatus(uint32 &user, uint32 &file)
417 user = 0;
418 file = 0;
419 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
420 const CServer* const curr = *it;
421 if( !curr->GetFailedCount() ) {
422 user += curr->GetUsers();
423 file += curr->GetFiles();
429 CServerList::~CServerList()
431 SaveServerMet();
432 while ( !m_servers.empty() ) {
433 delete m_servers.back();
434 m_servers.pop_back();
439 void CServerList::LoadStaticServers( const wxString& filename )
441 if ( !CPath::FileExists( filename ) ) {
442 return;
445 wxFileInputStream stream( filename );
446 wxTextInputStream f(stream);
448 while ( !stream.Eof() ) {
449 wxString line = f.ReadLine();
451 // Skip comments
452 if ( line.GetChar(0) == '#' || line.GetChar(0) == '/') {
453 continue;
456 wxStringTokenizer tokens( line, wxT(",") );
458 if ( tokens.CountTokens() != 3 ) {
459 continue;
463 // format is host:port,priority,Name
464 wxString addy = tokens.GetNextToken().Strip( wxString::both );
465 wxString prio = tokens.GetNextToken().Strip( wxString::both );
466 wxString name = tokens.GetNextToken().Strip( wxString::both );
468 wxString host = addy.BeforeFirst( wxT(':') );
469 wxString port = addy.AfterFirst( wxT(':') );
472 int priority = StrToLong( prio );
473 if (priority < SRV_PR_MIN || priority > SRV_PR_MAX) {
474 priority = SRV_PR_NORMAL;
478 // We need a valid name for the list
479 if ( name.IsEmpty() ) {
480 name = addy;
484 // create server object and add it to the list
485 CServer* server = new CServer( StrToLong( port ), host );
487 server->SetListName( name );
488 server->SetIsStaticMember( true );
489 server->SetPreference( priority );
492 // Try to add the server to the list
493 if ( !theApp->AddServer( server ) ) {
494 delete server;
495 CServer* existing = GetServerByAddress( host, StrToULong( port ) );
496 if ( existing) {
497 existing->SetListName( name );
498 existing->SetIsStaticMember( true );
499 existing->SetPreference( priority );
501 Notify_ServerRefresh( existing );
508 struct ServerPriorityComparator {
509 // Return true iff lhs should strictly appear earlier in the list than rhs.
510 // In this case, we want higher priority servers to appear earlier.
511 bool operator()(const CServer* lhs, const CServer* rhs) {
512 wxASSERT
514 rhs->GetPreferences() == SRV_PR_LOW ||
515 rhs->GetPreferences() == SRV_PR_NORMAL ||
516 rhs->GetPreferences() == SRV_PR_HIGH
518 switch (lhs->GetPreferences()) {
519 case SRV_PR_LOW:
520 return false;
521 case SRV_PR_NORMAL:
522 return rhs->GetPreferences() == SRV_PR_LOW;
523 case SRV_PR_HIGH:
524 return rhs->GetPreferences() != SRV_PR_HIGH;
525 default:
526 wxASSERT(0);
527 return false;
532 void CServerList::Sort()
534 m_servers.sort(ServerPriorityComparator());
535 // Once the list has been sorted, it doesn't really make sense to continue
536 // traversing the new order from the old position. Plus, there's a bug in
537 // version of libstdc++ before gcc4 such that iterators that were equal to
538 // end() were left dangling.
539 m_serverpos = m_servers.begin();
540 m_statserverpos = m_servers.begin();
544 CServer* CServerList::GetNextServer(bool bOnlyObfuscated)
546 while (bOnlyObfuscated && (m_serverpos != m_servers.end()) && !((*m_serverpos)->SupportsObfuscationTCP() || (*m_serverpos)->SupportsObfuscationUDP())) {
547 wxASSERT(*m_serverpos != NULL);
548 ++m_serverpos;
551 if (m_serverpos == m_servers.end()) {
552 return 0;
553 } else {
554 if (*m_serverpos) {
555 return *m_serverpos++;
556 } else {
557 return 0;
563 CServer* CServerList::GetNextStatServer()
565 if (m_statserverpos == m_servers.end()) {
566 m_statserverpos = m_servers.begin();
569 if (m_statserverpos == m_servers.end()) {
570 return 0;
571 } else {
572 wxASSERT(*m_statserverpos != NULL);
573 return *m_statserverpos++;
578 CServer* CServerList::GetServerByAddress(const wxString& address, uint16 port) const
580 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
581 CServer* const s = *it;
582 if (port == s->GetPort() && s->GetAddress() == address) {
583 return s;
586 return NULL;
590 CServer* CServerList::GetServerByIP(uint32 nIP) const
592 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it){
593 CServer* const s = *it;
594 if (s->GetIP() == nIP)
595 return s;
597 return NULL;
601 CServer* CServerList::GetServerByIPTCP(uint32 nIP, uint16 nPort) const
603 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it){
604 CServer* const s = *it;
605 if (s->GetIP() == nIP && s->GetPort() == nPort)
606 return s;
608 return NULL;
611 CServer* CServerList::GetServerByIPUDP(uint32 nIP, uint16 nUDPPort, bool bObfuscationPorts) const
613 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it){
614 CServer* const s =*it;
615 if (s->GetIP() == nIP && (s->GetPort() == nUDPPort-4 ||
616 (bObfuscationPorts && (s->GetObfuscationPortUDP() == nUDPPort) || (s->GetPort() == nUDPPort - 12))))
617 return s;
619 return NULL;
622 bool CServerList::SaveServerMet()
624 CPath newservermet = CPath(theApp->ConfigDir + wxT("server.met.new"));
626 CFile servermet( newservermet, CFile::write );
627 if (!servermet.IsOpened()) {
628 AddLogLineM(false,_("Failed to save server.met!"));
629 return false;
633 try {
634 servermet.WriteUInt8(0xE0);
635 servermet.WriteUInt32( m_servers.size() );
637 for ( CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
638 const CServer* const server = *it;
640 uint16 tagcount = 12;
641 if (!server->GetListName().IsEmpty()) {
642 ++tagcount;
644 if (!server->GetDynIP().IsEmpty()) {
645 ++tagcount;
647 if (!server->GetDescription().IsEmpty()) {
648 ++tagcount;
650 if (server->GetConnPort() != server->GetPort()) {
651 ++tagcount;
654 // For unicoded name, description, and dynip
655 if ( !server->GetListName().IsEmpty() ) {
656 ++tagcount;
658 if ( !server->GetDynIP().IsEmpty() ) {
659 ++tagcount;
661 if ( !server->GetDescription().IsEmpty() ) {
662 ++tagcount;
664 if (!server->GetVersion().IsEmpty()) {
665 ++tagcount;
668 if (server->GetServerKeyUDP(true)) {
669 ++tagcount;
672 if (server->GetServerKeyUDPIP()) {
673 ++tagcount;
676 if (server->GetObfuscationPortTCP()) {
677 ++tagcount;
680 if (server->GetObfuscationPortUDP()) {
681 ++tagcount;
684 servermet.WriteUInt32(server->GetIP());
685 servermet.WriteUInt16(server->GetPort());
686 servermet.WriteUInt32(tagcount);
688 if ( !server->GetListName().IsEmpty() ) {
689 // This is BOM to keep eMule compatibility
690 CTagString( ST_SERVERNAME, server->GetListName()).WriteTagToFile( &servermet, utf8strOptBOM);
691 CTagString( ST_SERVERNAME, server->GetListName()).WriteTagToFile( &servermet );
694 if ( !server->GetDynIP().IsEmpty() ) {
695 // This is BOM to keep eMule compatibility
696 CTagString( ST_DYNIP, server->GetDynIP()).WriteTagToFile( &servermet, utf8strOptBOM );
697 CTagString( ST_DYNIP, server->GetDynIP()).WriteTagToFile( &servermet );
700 if ( !server->GetDescription().IsEmpty() ) {
701 // This is BOM to keep eMule compatibility
702 CTagString( ST_DESCRIPTION, server->GetDescription()).WriteTagToFile( &servermet, utf8strOptBOM );
703 CTagString( ST_DESCRIPTION, server->GetDescription()).WriteTagToFile( &servermet );
706 if ( server->GetConnPort() != server->GetPort() ) {
707 CTagString( ST_AUXPORTSLIST, server->GetAuxPortsList() ).WriteTagToFile( &servermet );
710 CTagInt32( ST_FAIL, server->GetFailedCount() ).WriteTagToFile( &servermet );
711 CTagInt32( ST_PREFERENCE, server->GetPreferences() ).WriteTagToFile( &servermet );
712 CTagInt32( wxT("users"), server->GetUsers() ).WriteTagToFile( &servermet );
713 CTagInt32( wxT("files"), server->GetFiles() ).WriteTagToFile( &servermet );
714 CTagInt32( ST_PING, server->GetPing() ).WriteTagToFile( &servermet );
715 CTagInt32( ST_LASTPING, server->GetLastPingedTime()).WriteTagToFile( &servermet );
716 CTagInt32( ST_MAXUSERS, server->GetMaxUsers() ).WriteTagToFile( &servermet );
717 CTagInt32( ST_SOFTFILES, server->GetSoftFiles() ).WriteTagToFile( &servermet );
718 CTagInt32( ST_HARDFILES, server->GetHardFiles() ).WriteTagToFile( &servermet );
719 if (!server->GetVersion().IsEmpty()){
720 CTagString( ST_VERSION, server->GetVersion() ).WriteTagToFile( &servermet, utf8strOptBOM );
721 CTagString( ST_VERSION, server->GetVersion() ).WriteTagToFile( &servermet );
723 CTagInt32( ST_UDPFLAGS, server->GetUDPFlags() ).WriteTagToFile( &servermet );
724 CTagInt32( ST_LOWIDUSERS, server->GetLowIDUsers() ).WriteTagToFile( &servermet );
726 if (server->GetServerKeyUDP(true)) {
727 CTagInt32(ST_UDPKEY, server->GetServerKeyUDP(true)).WriteTagToFile( &servermet );;
730 if (server->GetServerKeyUDPIP()) {
731 CTagInt32(ST_UDPKEYIP, server->GetServerKeyUDPIP()).WriteTagToFile( &servermet );;;
734 if (server->GetObfuscationPortTCP()) {
735 CTagInt16(ST_TCPPORTOBFUSCATION, server->GetObfuscationPortTCP()).WriteTagToFile( &servermet );;;
738 if (server->GetObfuscationPortUDP()) {
739 CTagInt16(ST_UDPPORTOBFUSCATION, server->GetObfuscationPortUDP()).WriteTagToFile( &servermet );;;
743 } catch (const CIOFailureException& e) {
744 AddLogLineM(true, wxT("IO failure while writing 'server.met': ") + e.what());
745 return false;
748 servermet.Close();
749 const CPath curservermet = CPath(theApp->ConfigDir + wxT("server.met"));
750 const CPath oldservermet = CPath(theApp->ConfigDir + wxT("server_met.old"));
752 if (oldservermet.FileExists()) {
753 CPath::RemoveFile(oldservermet);
756 if (curservermet.FileExists()) {
757 CPath::RenameFile(curservermet, oldservermet);
760 CPath::RenameFile(newservermet, curservermet);
762 return true;
766 void CServerList::RemoveDeadServers()
768 if ( thePrefs::DeadServer() ) {
769 for ( CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ) {
770 CServer* server = *it++;
771 if ( server->GetFailedCount() > thePrefs::GetDeadserverRetries() && !server->IsStaticMember()) {
772 RemoveServer(server);
778 void CServerList::UpdateServerMetFromURL(const wxString& strURL)
780 if (strURL.Find(wxT("://")) == -1) {
781 AddLogLineM(true, _("Invalid URL"));
782 return;
784 URLUpdate = strURL;
785 wxString strTempFilename(theApp->ConfigDir + wxT("server.met.download"));
786 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(strURL,strTempFilename, HTTP_ServerMet);
787 downloader->Create();
788 downloader->Run();
792 void CServerList::DownloadFinished(uint32 result)
794 if(result == 1) {
795 const CPath tempFilename = CPath(theApp->ConfigDir + wxT("server.met.download"));
797 // curl succeeded. proceed with server.met loading
798 LoadServerMet(tempFilename);
799 SaveServerMet();
801 // So, file is loaded and merged, and also saved
802 CPath::RemoveFile(tempFilename);
803 AddLogLineM(true, CFormat(
804 _("Finished to download the server list from %s")) % URLUpdate);
805 } else {
806 AddLogLineM(true, CFormat( _("Failed to download the server list from %s") ) % URLUpdate);
811 void CServerList::AutoUpdate()
814 uint8 url_count = theApp->glob_prefs->adresses_list.GetCount();
816 if (!url_count) {
817 AddLogLineM(true, _("No serverlist address entry in 'addresses.dat' found. Please paste a valid serverlist address into this file in order to auto-update your serverlist"));
818 return;
820 // Do current URL. Callback function will take care of the others.
821 while ( current_url_index < url_count ) {
822 wxString URI = theApp->glob_prefs->adresses_list[current_url_index];
823 // We use wxURL to validate the URI
824 if ( wxURL( URI ).GetError() == wxURL_NOERR ) {
825 // Ok, got a valid URI
826 URLAutoUpdate = URI;
827 wxString strTempFilename =
828 theApp->ConfigDir + wxT("server_auto.met");
829 AddLogLineM(true, CFormat(
830 _("Start downloading server list from %s")) % URI);
831 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(
832 URI, strTempFilename, HTTP_ServerMetAuto);
833 downloader->Create();
834 downloader->Run();
836 return;
837 } else {
838 AddLogLineM(true, CFormat(
839 _("Warning, invalid URL specified for auto-updating of servers: %s") ) % URI);
841 current_url_index++;
843 AddLogLineM(true, _("No valid server.met auto-download url on addresses.dat"));
847 void CServerList::AutoDownloadFinished(uint32 result)
850 if (result == 1) {
851 CPath tempFilename = CPath(theApp->ConfigDir + wxT("server_auto.met"));
853 // curl succeeded. proceed with server.met loading
854 LoadServerMet(tempFilename);
855 SaveServerMet();
857 // So, file is loaded and merged, and also saved
858 CPath::RemoveFile(tempFilename);
859 } else {
860 AddLogLineM(true, CFormat(_("Failed to download the server list from %s") ) % URLUpdate);
863 ++current_url_index;
866 if (current_url_index < theApp->glob_prefs->adresses_list.GetCount()) {
867 // Next!
868 AutoUpdate();
874 void CServerList::ObserverAdded( ObserverType* o )
876 CObservableQueue<CServer*>::ObserverAdded( o );
878 EventType::ValueList ilist;
879 ilist.reserve( m_servers.size() );
880 ilist.assign( m_servers.begin(), m_servers.end() );
882 NotifyObservers( EventType( EventType::INITIAL, &ilist ), o );
886 uint32 CServerList::GetAvgFile() const
888 //Since there is no real way to know how many files are in the kad network,
889 //I figure to try to use the ED2K network stats to find how many files the
890 //average user shares..
891 uint32 totaluser = 0;
892 uint32 totalfile = 0;
893 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it){
894 const CServer* const curr = *it;
895 //If this server has reported Users/Files and doesn't limit it's files too much
896 //use this in the calculation..
897 if( curr->GetUsers() && curr->GetFiles() && curr->GetSoftFiles() > 1000 ) {
898 totaluser += curr->GetUsers();
899 totalfile += curr->GetFiles();
902 //If the user count is a little low, don't send back a average..
903 //I added 50 to the count as many servers do not allow a large amount of files to be shared..
904 //Therefore the estimate here will be lower then the actual.
905 //I would love to add a way for the client to send some statistics back so we could see the real
906 //values here..
907 if ( totaluser > 500000 ) {
908 return (totalfile/totaluser)+50;
909 } else {
910 return 0;
915 std::vector<const CServer*> CServerList::CopySnapshot() const
917 std::vector<const CServer*> result;
918 result.reserve(m_servers.size());
919 result.assign(m_servers.begin(), m_servers.end());
920 return result;
924 void CServerList::FilterServers()
926 CInternalList::iterator it = m_servers.begin();
927 while (it != m_servers.end()) {
928 CServer* server = *it++;
930 if (server->HasDynIP()) {
931 continue;
934 if (theApp->ipfilter->IsFiltered(server->GetIP(), true)) {
935 if (server == theApp->serverconnect->GetCurrentServer()) {
936 AddLogLineM(true, _("Local server is filtered by the IPFilters, reconnecting to a different server!"));
937 theApp->serverconnect->Disconnect();
938 RemoveServer(server);
939 theApp->serverconnect->ConnectToAnyServer();
940 } else {
941 RemoveServer(server);
947 void CServerList::CheckForExpiredUDPKeys() {
949 if (!thePrefs::IsServerCryptLayerUDPEnabled()) {
950 return;
953 uint32 cKeysTotal = 0;
954 uint32 cKeysExpired = 0;
955 uint32 cPingDelayed = 0;
956 const uint32 dwIP = theApp->GetPublicIP();
957 const uint32 tNow = ::GetTickCount();
958 wxASSERT( dwIP != 0 );
960 for (CInternalList::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it) {
961 CServer* pServer = *it;
962 if (pServer->SupportsObfuscationUDP() && pServer->GetServerKeyUDP(true) != 0 && pServer->GetServerKeyUDPIP() != dwIP){
963 cKeysTotal++;
964 cKeysExpired++;
965 if (tNow - pServer->GetRealLastPingedTime() < UDPSERVSTATMINREASKTIME){
966 cPingDelayed++;
967 // next ping: Now + (MinimumDelay - already elapsed time)
968 pServer->SetLastPingedTime((tNow - (uint32)UDPSERVSTATREASKTIME) + (UDPSERVSTATMINREASKTIME - (tNow - pServer->GetRealLastPingedTime())));
969 } else {
970 pServer->SetLastPingedTime(0);
972 } else if (pServer->SupportsObfuscationUDP() && pServer->GetServerKeyUDP(false) != 0) {
973 cKeysTotal++;
977 // File_checked_for_headers