Release 2.3.3
[amule.git] / src / ExternalConn.cpp
blob4b5bd4d22bb93ac29ce4ffbd849725bc1aa2261f
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 Kry ( elkry@sourceforge.net / http://www.amule.org )
5 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
6 // Copyright (c) 2008-2011 Froenchenko Leonid (lfroen@gmail.com)
7 //
8 // Any parts of this program derived from the xMule, lMule or eMule project,
9 // or contributed by third-party developers are copyrighted by their
10 // respective authors.
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 2 of the License, or
15 // (at your option) any later version.
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "config.h" // Needed for VERSION
29 #include <ec/cpp/ECMuleSocket.h> // Needed for CECSocket
31 #include <common/Format.h> // Needed for CFormat
33 #include <common/ClientVersion.h>
34 #include <common/MD5Sum.h>
36 #include "ExternalConn.h" // Interface declarations
37 #include "updownclient.h" // Needed for CUpDownClient
38 #include "Server.h" // Needed for CServer
39 #include "ServerList.h" // Needed for CServerList
40 #include "PartFile.h" // Needed for CPartFile
41 #include "ServerConnect.h" // Needed for CServerConnect
42 #include "UploadQueue.h" // Needed for CUploadQueue
43 #include "amule.h" // Needed for theApp
44 #include "SearchList.h" // Needed for GetSearchResults
45 #include "ClientList.h"
46 #include "Preferences.h" // Needed for CPreferences
47 #include "Logger.h"
48 #include "GuiEvents.h" // Needed for Notify_* macros
49 #include "Statistics.h" // Needed for theStats
50 #include "KnownFileList.h" // Needed for CKnownFileList
51 #include "Friend.h"
52 #include "FriendList.h"
53 #include "RandomFunctions.h"
54 #include "kademlia/kademlia/Kademlia.h"
55 #include "kademlia/kademlia/UDPFirewallTester.h"
56 #include "Statistics.h"
59 //-------------------- File_Encoder --------------------
63 * Encode 'obtained parts' info to be sent to remote gui
65 class CKnownFile_Encoder {
66 // number of sources for each part for progress bar colouring
67 RLE_Data m_enc_data;
68 protected:
69 const CKnownFile *m_file;
70 public:
71 CKnownFile_Encoder(const CKnownFile *file = 0) { m_file = file; }
73 virtual ~CKnownFile_Encoder() {}
75 virtual void Encode(CECTag *parent_tag);
77 virtual void ResetEncoder()
79 m_enc_data.ResetEncoder();
82 virtual void SetShared() { }
83 virtual bool IsShared() { return true; }
84 virtual bool IsPartFile_Encoder() { return false; }
85 const CKnownFile * GetFile() { return m_file; }
88 /*!
89 * PartStatus strings and gap lists are quite long - RLE encoding will help.
91 * Instead of sending each time full part-status string, send
92 * RLE encoded difference from previous one.
94 * PartFileEncoderData class is used for decode only,
95 * while CPartFile_Encoder is used for encode only.
97 class CPartFile_Encoder : public CKnownFile_Encoder {
98 // blocks requested for download
99 RLE_Data m_req_status;
100 // gap list
101 RLE_Data m_gap_status;
102 // source names
103 SourcenameItemMap m_sourcenameItemMap;
104 // counter for unique source name ids
105 int m_sourcenameID;
106 // not all part files are shared (only when at least one part is complete)
107 bool m_shared;
109 // cast inherited member to CPartFile
110 const CPartFile * m_PartFile() { wxCHECK(m_file->IsCPartFile(), NULL); return static_cast<const CPartFile *>(m_file); }
111 public:
112 // encoder side
113 CPartFile_Encoder(const CPartFile *file = 0) : CKnownFile_Encoder(file)
115 m_sourcenameID = 0;
116 m_shared = false;
119 virtual ~CPartFile_Encoder() {}
121 // encode - take data from m_file
122 virtual void Encode(CECTag *parent_tag);
124 // Encoder may reset history if full info requested
125 virtual void ResetEncoder();
127 virtual void SetShared() { m_shared = true; }
128 virtual bool IsShared() { return m_shared; }
129 virtual bool IsPartFile_Encoder() { return true; }
132 class CFileEncoderMap : public std::map<uint32, CKnownFile_Encoder*> {
133 typedef std::set<uint32> IDSet;
134 public:
135 ~CFileEncoderMap();
136 void UpdateEncoders();
139 CFileEncoderMap::~CFileEncoderMap()
141 // DeleteContents() causes infinite recursion here!
142 for (iterator it = begin(); it != end(); ++it) {
143 delete it->second;
147 // Check if encoder contains files that are no longer used
148 // or if we have new files without encoder yet.
149 void CFileEncoderMap::UpdateEncoders()
151 IDSet curr_files, dead_files;
152 // Downloads
153 std::vector<CPartFile*> downloads;
154 theApp->downloadqueue->CopyFileList(downloads, true);
155 for (uint32 i = downloads.size(); i--;) {
156 uint32 id = downloads[i]->ECID();
157 curr_files.insert(id);
158 if (!count(id)) {
159 (*this)[id] = new CPartFile_Encoder(downloads[i]);
162 // Shares
163 std::vector<CKnownFile*> shares;
164 theApp->sharedfiles->CopyFileList(shares);
165 for (uint32 i = shares.size(); i--;) {
166 uint32 id = shares[i]->ECID();
167 // Check if it is already there.
168 // The curr_files.count(id) is enough, the IsCPartFile() is just a speedup.
169 if (shares[i]->IsCPartFile() && curr_files.count(id)) {
170 (*this)[id]->SetShared();
171 continue;
173 curr_files.insert(id);
174 if (!count(id)) {
175 (*this)[id] = new CKnownFile_Encoder(shares[i]);
178 // Check for removed files, and store them in a set for deletion.
179 // (std::map documentation is unclear if a construct like
180 // iterator to_del = it++; erase(to_del) ;
181 // works or invalidates it too.)
182 for (iterator it = begin(); it != end(); ++it) {
183 if (!curr_files.count(it->first)) {
184 dead_files.insert(it->first);
187 // then delete them
188 for (IDSet::iterator it = dead_files.begin(); it != dead_files.end(); ++it) {
189 iterator it2 = find(*it);
190 delete it2->second;
191 erase(it2);
196 //-------------------- CECServerSocket --------------------
198 class CECServerSocket : public CECMuleSocket
200 public:
201 CECServerSocket(ECNotifier *notifier);
202 virtual ~CECServerSocket();
204 virtual const CECPacket *OnPacketReceived(const CECPacket *packet, uint32 trueSize);
205 virtual void OnLost();
207 virtual void WriteDoneAndQueueEmpty();
209 void ResetLog() { m_LoggerAccess.Reset(); }
210 private:
211 ECNotifier *m_ec_notifier;
213 const CECPacket *Authenticate(const CECPacket *);
215 enum {
216 CONN_INIT,
217 CONN_SALT_SENT,
218 CONN_ESTABLISHED,
219 CONN_FAILED
220 } m_conn_state;
222 uint64_t m_passwd_salt;
223 CLoggerAccess m_LoggerAccess;
224 CFileEncoderMap m_FileEncoder;
225 CObjTagMap m_obj_tagmap;
226 CECPacket *ProcessRequest2(const CECPacket *request);
228 virtual bool IsAuthorized() { return m_conn_state == CONN_ESTABLISHED; }
232 CECServerSocket::CECServerSocket(ECNotifier *notifier)
234 CECMuleSocket(true),
235 m_conn_state(CONN_INIT),
236 m_passwd_salt(GetRandomUint64())
238 wxASSERT(theApp->ECServerHandler);
239 theApp->ECServerHandler->AddSocket(this);
240 m_ec_notifier = notifier;
244 CECServerSocket::~CECServerSocket()
246 wxASSERT(theApp->ECServerHandler);
247 theApp->ECServerHandler->RemoveSocket(this);
251 const CECPacket *CECServerSocket::OnPacketReceived(const CECPacket *packet, uint32 trueSize)
253 packet->DebugPrint(true, trueSize);
255 const CECPacket *reply = NULL;
257 if (m_conn_state == CONN_FAILED) {
258 // Client didn't close the socket when authentication failed.
259 AddLogLineN(_("Client sent packet after authentication failed."));
260 CloseSocket();
263 if (m_conn_state != CONN_ESTABLISHED) {
264 // This is called twice:
265 // 1) send salt
266 // 2) verify password
267 reply = Authenticate(packet);
268 } else {
269 reply = ProcessRequest2(packet);
271 return reply;
275 void CECServerSocket::OnLost()
277 AddLogLineN(_("External connection closed."));
278 theApp->ECServerHandler->m_ec_notifier->Remove_EC_Client(this);
279 DestroySocket();
282 void CECServerSocket::WriteDoneAndQueueEmpty()
284 if ( HaveNotificationSupport() && (m_conn_state == CONN_ESTABLISHED) ) {
285 CECPacket *packet = m_ec_notifier->GetNextPacket(this);
286 if ( packet ) {
287 SendPacket(packet);
289 } else {
290 //printf("[EC] %p: WriteDoneAndQueueEmpty but notification disabled\n", this);
294 //-------------------- ExternalConn --------------------
296 #ifndef ASIO_SOCKETS
297 enum
298 { // id for sockets
299 SERVER_ID = 1000
303 BEGIN_EVENT_TABLE(ExternalConn, wxEvtHandler)
304 EVT_SOCKET(SERVER_ID, ExternalConn::OnServerEvent)
305 END_EVENT_TABLE()
306 #endif
309 ExternalConn::ExternalConn(amuleIPV4Address addr, wxString *msg)
311 wxString msgLocal;
312 m_ECServer = NULL;
313 // Are we allowed to accept External Connections?
314 if ( thePrefs::AcceptExternalConnections() ) {
315 // We must have a valid password, otherwise we will not allow EC connections
316 if (thePrefs::ECPassword().IsEmpty()) {
317 *msg += wxT("External connections disabled due to empty password!\n");
318 AddLogLineC(_("External connections disabled due to empty password!"));
319 return;
322 // Create the socket
323 m_ECServer = new CExternalConnListener(addr, MULE_SOCKET_REUSEADDR, this);
324 #ifndef ASIO_SOCKETS
325 m_ECServer->SetEventHandler(*this, SERVER_ID);
326 m_ECServer->SetNotify(wxSOCKET_CONNECTION_FLAG);
327 #endif
328 m_ECServer->Notify(true);
330 int port = addr.Service();
331 wxString ip = addr.IPAddress();
332 if (m_ECServer->IsOk()) {
333 msgLocal = CFormat(wxT("*** TCP socket (ECServer) listening on %s:%d")) % ip % port;
334 *msg += msgLocal + wxT("\n");
335 AddLogLineN(msgLocal);
336 } else {
337 msgLocal = CFormat(wxT("Could not listen for external connections at %s:%d!")) % ip % port;
338 *msg += msgLocal + wxT("\n");
339 AddLogLineN(msgLocal);
341 } else {
342 *msg += wxT("External connections disabled in config file\n");
343 AddLogLineN(_("External connections disabled in config file"));
345 m_ec_notifier = new ECNotifier();
349 ExternalConn::~ExternalConn()
351 KillAllSockets();
352 delete m_ECServer;
353 delete m_ec_notifier;
357 void ExternalConn::AddSocket(CECServerSocket *s)
359 wxASSERT(s);
360 socket_list.insert(s);
364 void ExternalConn::RemoveSocket(CECServerSocket *s)
366 wxASSERT(s);
367 socket_list.erase(s);
371 void ExternalConn::KillAllSockets()
373 AddDebugLogLineN(logGeneral,
374 CFormat(wxT("ExternalConn::KillAllSockets(): %d sockets to destroy.")) %
375 socket_list.size());
376 SocketSet::iterator it = socket_list.begin();
377 while (it != socket_list.end()) {
378 CECServerSocket *s = *(it++);
379 s->Close();
380 s->Destroy();
382 socket_list.clear();
386 void ExternalConn::ResetAllLogs()
388 SocketSet::iterator it = socket_list.begin();
389 while (it != socket_list.end()) {
390 CECServerSocket *s = *(it++);
391 s->ResetLog();
396 #ifndef ASIO_SOCKETS
397 void ExternalConn::OnServerEvent(wxSocketEvent& WXUNUSED(event))
399 m_ECServer->OnAccept();
401 #endif
404 void CExternalConnListener::OnAccept()
406 CECServerSocket *sock = new CECServerSocket(m_conn->m_ec_notifier);
407 // Accept new connection if there is one in the pending
408 // connections queue, else exit. We use Accept(FALSE) for
409 // non-blocking accept (although if we got here, there
410 // should ALWAYS be a pending connection).
411 if (AcceptWith(*sock, false)) {
412 AddLogLineN(_("New external connection accepted"));
413 } else {
414 delete sock;
415 AddLogLineN(_("ERROR: couldn't accept a new external connection"));
421 // Authentication
423 const CECPacket *CECServerSocket::Authenticate(const CECPacket *request)
425 CECPacket *response;
427 if (request == NULL) {
428 return new CECPacket(EC_OP_AUTH_FAIL);
431 // Password must be specified if we are to allow remote connections
432 if ( thePrefs::ECPassword().IsEmpty() ) {
433 AddLogLineC(_("External connection refused due to empty password in preferences!"));
435 return new CECPacket(EC_OP_AUTH_FAIL);
438 if ((m_conn_state == CONN_INIT) && (request->GetOpCode() == EC_OP_AUTH_REQ) ) {
439 // cppcheck-suppress unreadVariable
440 const CECTag *clientName = request->GetTagByName(EC_TAG_CLIENT_NAME);
441 // cppcheck-suppress unreadVariable
442 const CECTag *clientVersion = request->GetTagByName(EC_TAG_CLIENT_VERSION);
444 AddLogLineN(CFormat( _("Connecting client: %s %s") )
445 % ( clientName ? clientName->GetStringData() : wxString(_("Unknown")) )
446 % ( clientVersion ? clientVersion->GetStringData() : wxString(_("Unknown version")) ) );
447 const CECTag *protocol = request->GetTagByName(EC_TAG_PROTOCOL_VERSION);
448 #ifdef EC_VERSION_ID
449 // For SVN versions, both client and server must use SVNDATE, and they must be the same
450 CMD4Hash vhash;
451 if (!vhash.Decode(wxT(EC_VERSION_ID))) {
452 response = new CECPacket(EC_OP_AUTH_FAIL);
453 response->AddTag(CECTag(EC_TAG_STRING, wxT("Fatal error, version hash is not a valid MD4-hash.")));
454 } else if (!request->GetTagByName(EC_TAG_VERSION_ID) || request->GetTagByNameSafe(EC_TAG_VERSION_ID)->GetMD4Data() != vhash) {
455 response = new CECPacket(EC_OP_AUTH_FAIL);
456 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Incorrect EC version ID, there might be binary incompatibility. Use core and remote from same snapshot.")));
457 #else
458 // For release versions, we don't want to allow connections from any arbitrary SVN client.
459 if (request->GetTagByName(EC_TAG_VERSION_ID)) {
460 response = new CECPacket(EC_OP_AUTH_FAIL);
461 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("You cannot connect to a release version from an arbitrary development snapshot! *sigh* possible crash prevented")));
462 #endif
463 } else if (protocol != NULL) {
464 uint16 proto_version = protocol->GetInt();
465 if (proto_version == EC_CURRENT_PROTOCOL_VERSION) {
466 response = new CECPacket(EC_OP_AUTH_SALT);
467 response->AddTag(CECTag(EC_TAG_PASSWD_SALT, m_passwd_salt));
468 m_conn_state = CONN_SALT_SENT;
470 // So far ok, check capabilities of client
472 if (request->GetTagByName(EC_TAG_CAN_ZLIB)) {
473 m_my_flags |= EC_FLAG_ZLIB;
475 if (request->GetTagByName(EC_TAG_CAN_UTF8_NUMBERS)) {
476 m_my_flags |= EC_FLAG_UTF8_NUMBERS;
478 m_haveNotificationSupport = request->GetTagByName(EC_TAG_CAN_NOTIFY) != NULL;
479 AddDebugLogLineN(logEC, CFormat(wxT("Client capabilities: ZLIB: %s UTF8 numbers: %s Push notification: %s") )
480 % ((m_my_flags & EC_FLAG_ZLIB) ? wxT("yes") : wxT("no"))
481 % ((m_my_flags & EC_FLAG_UTF8_NUMBERS) ? wxT("yes") : wxT("no"))
482 % (m_haveNotificationSupport ? wxT("yes") : wxT("no")));
483 } else {
484 response = new CECPacket(EC_OP_AUTH_FAIL);
485 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Invalid protocol version.")
486 + CFormat(wxT("( %#.4x != %#.4x )")) % proto_version % (uint16_t)EC_CURRENT_PROTOCOL_VERSION));
488 } else {
489 response = new CECPacket(EC_OP_AUTH_FAIL);
490 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Missing protocol version tag.")));
492 } else if ((m_conn_state == CONN_SALT_SENT) && (request->GetOpCode() == EC_OP_AUTH_PASSWD)) {
493 const CECTag *passwd = request->GetTagByName(EC_TAG_PASSWD_HASH);
494 CMD4Hash passh;
496 if (!passh.Decode(thePrefs::ECPassword())) {
497 wxString err = wxTRANSLATE("Authentication failed: invalid hash specified as EC password.");
498 AddLogLineN(wxString(wxGetTranslation(err))
499 + wxT(" ") + thePrefs::ECPassword());
500 response = new CECPacket(EC_OP_AUTH_FAIL);
501 response->AddTag(CECTag(EC_TAG_STRING, err));
502 } else {
503 wxString saltHash = MD5Sum(CFormat(wxT("%lX")) % m_passwd_salt).GetHash();
504 wxString saltStr = CFormat(wxT("%lX")) % m_passwd_salt;
506 passh.Decode(MD5Sum(thePrefs::ECPassword().Lower() + saltHash).GetHash());
508 if (passwd && passwd->GetMD4Data() == passh) {
509 response = new CECPacket(EC_OP_AUTH_OK);
510 response->AddTag(CECTag(EC_TAG_SERVER_VERSION, wxT(VERSION)));
511 } else {
512 wxString err;
513 if (passwd) {
514 err = wxTRANSLATE("Authentication failed: wrong password.");
515 } else {
516 err = wxTRANSLATE("Authentication failed: missing password.");
519 response = new CECPacket(EC_OP_AUTH_FAIL);
520 response->AddTag(CECTag(EC_TAG_STRING, err));
521 AddLogLineN(wxGetTranslation(err));
524 } else {
525 response = new CECPacket(EC_OP_AUTH_FAIL);
526 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Invalid request, please authenticate first.")));
529 if (response->GetOpCode() == EC_OP_AUTH_OK) {
530 m_conn_state = CONN_ESTABLISHED;
531 AddLogLineN(_("Access granted."));
532 // Establish notification handler if client supports it
533 if (HaveNotificationSupport()) {
534 theApp->ECServerHandler->m_ec_notifier->Add_EC_Client(this);
536 } else if (response->GetOpCode() == EC_OP_AUTH_FAIL) {
537 // Log message sent to client
538 if (response->GetFirstTagSafe()->IsString()) {
539 AddLogLineN(CFormat(_("Sent error message \"%s\" to client.")) % wxGetTranslation(response->GetFirstTagSafe()->GetStringData()));
541 // Access denied!
542 AddLogLineN(CFormat(_("Unauthorized access attempt from %s. Connection closed.")) % GetPeer() );
543 m_conn_state = CONN_FAILED;
546 return response;
549 // Make a Logger tag (if there are any logging messages) and add it to the response
550 static void AddLoggerTag(CECPacket *response, CLoggerAccess &LoggerAccess)
552 if (LoggerAccess.HasString()) {
553 CECEmptyTag tag(EC_TAG_STATS_LOGGER_MESSAGE);
554 // Tag structure is fix: tag carries nothing, inside are the strings
555 // maximum of 200 log lines per message
556 int entries = 0;
557 wxString line;
558 while (entries < 200 && LoggerAccess.GetString(line)) {
559 tag.AddTag(CECTag(EC_TAG_STRING, line));
560 entries++;
562 response->AddTag(tag);
563 //printf("send Log tag %d %d\n", FirstEntry, entries);
567 static CECPacket *Get_EC_Response_StatRequest(const CECPacket *request, CLoggerAccess &LoggerAccess)
569 CECPacket *response = new CECPacket(EC_OP_STATS);
571 switch (request->GetDetailLevel()) {
572 case EC_DETAIL_FULL:
573 // This is not an actual INC_UPDATE.
574 // amulegui only sets the detail level of the stats package to EC_DETAIL_INC_UPDATE
575 // so that the included conn state tag is created the way it is needed here.
576 case EC_DETAIL_INC_UPDATE:
577 response->AddTag(CECTag(EC_TAG_STATS_UP_OVERHEAD, (uint32)theStats::GetUpOverheadRate()));
578 response->AddTag(CECTag(EC_TAG_STATS_DOWN_OVERHEAD, (uint32)theStats::GetDownOverheadRate()));
579 response->AddTag(CECTag(EC_TAG_STATS_BANNED_COUNT, /*(uint32)*/theStats::GetBannedCount()));
580 AddLoggerTag(response, LoggerAccess);
581 // Needed only for the remote tray icon context menu
582 response->AddTag(CECTag(EC_TAG_STATS_TOTAL_SENT_BYTES, theStats::GetTotalSentBytes()));
583 response->AddTag(CECTag(EC_TAG_STATS_TOTAL_RECEIVED_BYTES, theStats::GetTotalReceivedBytes()));
584 response->AddTag(CECTag(EC_TAG_STATS_SHARED_FILE_COUNT, theStats::GetSharedFileCount()));
585 /* fall through */
586 case EC_DETAIL_WEB:
587 case EC_DETAIL_CMD:
588 response->AddTag(CECTag(EC_TAG_STATS_UL_SPEED, (uint32)theStats::GetUploadRate()));
589 response->AddTag(CECTag(EC_TAG_STATS_DL_SPEED, (uint32)(theStats::GetDownloadRate())));
590 response->AddTag(CECTag(EC_TAG_STATS_UL_SPEED_LIMIT, (uint32)(thePrefs::GetMaxUpload()*1024.0)));
591 response->AddTag(CECTag(EC_TAG_STATS_DL_SPEED_LIMIT, (uint32)(thePrefs::GetMaxDownload()*1024.0)));
592 response->AddTag(CECTag(EC_TAG_STATS_UL_QUEUE_LEN, /*(uint32)*/theStats::GetWaitingUserCount()));
593 response->AddTag(CECTag(EC_TAG_STATS_TOTAL_SRC_COUNT, /*(uint32)*/theStats::GetFoundSources()));
594 // User/Filecounts
596 uint32 totaluser = 0, totalfile = 0;
597 theApp->serverlist->GetUserFileStatus( totaluser, totalfile );
598 response->AddTag(CECTag(EC_TAG_STATS_ED2K_USERS, totaluser));
599 response->AddTag(CECTag(EC_TAG_STATS_KAD_USERS, Kademlia::CKademlia::GetKademliaUsers()));
600 response->AddTag(CECTag(EC_TAG_STATS_ED2K_FILES, totalfile));
601 response->AddTag(CECTag(EC_TAG_STATS_KAD_FILES, Kademlia::CKademlia::GetKademliaFiles()));
602 response->AddTag(CECTag(EC_TAG_STATS_KAD_NODES, CStatistics::GetKadNodes()));
604 // Kad stats
605 if (Kademlia::CKademlia::IsConnected()) {
606 response->AddTag(CECTag(EC_TAG_STATS_KAD_FIREWALLED_UDP, Kademlia::CUDPFirewallTester::IsFirewalledUDP(true)));
607 response->AddTag(CECTag(EC_TAG_STATS_KAD_INDEXED_SOURCES, Kademlia::CKademlia::GetIndexed()->m_totalIndexSource));
608 response->AddTag(CECTag(EC_TAG_STATS_KAD_INDEXED_KEYWORDS, Kademlia::CKademlia::GetIndexed()->m_totalIndexKeyword));
609 response->AddTag(CECTag(EC_TAG_STATS_KAD_INDEXED_NOTES, Kademlia::CKademlia::GetIndexed()->m_totalIndexNotes));
610 response->AddTag(CECTag(EC_TAG_STATS_KAD_INDEXED_LOAD, Kademlia::CKademlia::GetIndexed()->m_totalIndexLoad));
611 response->AddTag(CECTag(EC_TAG_STATS_KAD_IP_ADRESS, wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetPrefs()->GetIPAddress())));
612 response->AddTag(CECTag(EC_TAG_STATS_KAD_IN_LAN_MODE, Kademlia::CKademlia::IsRunningInLANMode()));
613 response->AddTag(CECTag(EC_TAG_STATS_BUDDY_STATUS, theApp->clientlist->GetBuddyStatus()));
614 uint32 BuddyIP = 0;
615 uint16 BuddyPort = 0;
616 CUpDownClient * Buddy = theApp->clientlist->GetBuddy();
617 if (Buddy) {
618 BuddyIP = Buddy->GetIP();
619 BuddyPort = Buddy->GetUDPPort();
621 response->AddTag(CECTag(EC_TAG_STATS_BUDDY_IP, BuddyIP));
622 response->AddTag(CECTag(EC_TAG_STATS_BUDDY_PORT, BuddyPort));
624 case EC_DETAIL_UPDATE:
625 break;
628 return response;
631 static CECPacket *Get_EC_Response_GetSharedFiles(const CECPacket *request, CFileEncoderMap &encoders)
633 wxASSERT(request->GetOpCode() == EC_OP_GET_SHARED_FILES);
635 CECPacket *response = new CECPacket(EC_OP_SHARED_FILES);
637 EC_DETAIL_LEVEL detail_level = request->GetDetailLevel();
639 // request can contain list of queried items
640 CTagSet<uint32, EC_TAG_KNOWNFILE> queryitems(request);
642 encoders.UpdateEncoders();
644 for (uint32 i = 0; i < theApp->sharedfiles->GetFileCount(); ++i) {
645 const CKnownFile *cur_file = theApp->sharedfiles->GetFileByIndex(i);
647 if ( !cur_file || (!queryitems.empty() && !queryitems.count(cur_file->ECID())) ) {
648 continue;
651 CEC_SharedFile_Tag filetag(cur_file, detail_level);
652 CKnownFile_Encoder *enc = encoders[cur_file->ECID()];
653 if ( detail_level != EC_DETAIL_UPDATE ) {
654 enc->ResetEncoder();
656 enc->Encode(&filetag);
657 response->AddTag(filetag);
659 return response;
662 static CECPacket *Get_EC_Response_GetUpdate(CFileEncoderMap &encoders, CObjTagMap &tagmap)
664 CECPacket *response = new CECPacket(EC_OP_SHARED_FILES);
666 encoders.UpdateEncoders();
667 for (CFileEncoderMap::iterator it = encoders.begin(); it != encoders.end(); ++it) {
668 const CKnownFile *cur_file = it->second->GetFile();
669 CValueMap &valuemap = tagmap.GetValueMap(cur_file->ECID());
670 // Completed cleared Partfiles are still stored as CPartfile,
671 // but encoded as KnownFile, so we have to check the encoder type
672 // instead of the file type.
673 if (it->second->IsPartFile_Encoder()) {
674 CEC_PartFile_Tag filetag(static_cast<const CPartFile*>(cur_file), EC_DETAIL_INC_UPDATE, &valuemap);
675 // Add information if partfile is shared
676 filetag.AddTag(EC_TAG_PARTFILE_SHARED, it->second->IsShared(), &valuemap);
678 CPartFile_Encoder * enc = static_cast<CPartFile_Encoder *>(encoders[cur_file->ECID()]);
679 enc->Encode(&filetag);
680 response->AddTag(filetag);
681 } else {
682 CEC_SharedFile_Tag filetag(cur_file, EC_DETAIL_INC_UPDATE, &valuemap);
683 CKnownFile_Encoder * enc = encoders[cur_file->ECID()];
684 enc->Encode(&filetag);
685 response->AddTag(filetag);
689 // Add clients
690 CECEmptyTag clients(EC_TAG_CLIENT);
691 const CClientList::IDMap& clientList = theApp->clientlist->GetClientList();
692 bool onlyTransmittingClients = thePrefs::IsTransmitOnlyUploadingClients();
693 for (CClientList::IDMap::const_iterator it = clientList.begin(); it != clientList.end(); ++it) {
694 const CUpDownClient* cur_client = it->second.GetClient();
695 if (onlyTransmittingClients && !cur_client->IsDownloading()) {
696 // For poor CPU cores only transmit uploading clients. This will save a lot of CPU.
697 // Set ExternalConnect/TransmitOnlyUploadingClients to 1 for it.
698 continue;
700 CValueMap &valuemap = tagmap.GetValueMap(cur_client->ECID());
701 clients.AddTag(CEC_UpDownClient_Tag(cur_client, EC_DETAIL_INC_UPDATE, &valuemap));
703 response->AddTag(clients);
705 // Add servers
706 CECEmptyTag servers(EC_TAG_SERVER);
707 std::vector<const CServer*> serverlist = theApp->serverlist->CopySnapshot();
708 uint32 nrServers = serverlist.size();
709 for (uint32 i = 0; i < nrServers; i++) {
710 const CServer* cur_server = serverlist[i];
711 CValueMap &valuemap = tagmap.GetValueMap(cur_server->ECID());
712 servers.AddTag(CEC_Server_Tag(cur_server, &valuemap));
714 response->AddTag(servers);
716 // Add friends
717 CECEmptyTag friends(EC_TAG_FRIEND);
718 for (CFriendList::const_iterator it = theApp->friendlist->begin(); it != theApp->friendlist->end(); ++it) {
719 const CFriend* cur_friend = *it;
720 CValueMap &valuemap = tagmap.GetValueMap(cur_friend->ECID());
721 friends.AddTag(CEC_Friend_Tag(cur_friend, &valuemap));
723 response->AddTag(friends);
725 return response;
728 static CECPacket *Get_EC_Response_GetClientQueue(const CECPacket *request, CObjTagMap &tagmap, int op)
730 CECPacket *response = new CECPacket(op);
732 EC_DETAIL_LEVEL detail_level = request->GetDetailLevel();
735 // request can contain list of queried items
736 // (not for incremental update of course)
737 CTagSet<uint32, EC_TAG_CLIENT> queryitems(request);
739 const CClientRefList& clients = theApp->uploadqueue->GetUploadingList();
740 CClientRefList::const_iterator it = clients.begin();
741 for (; it != clients.end(); ++it) {
742 CUpDownClient* cur_client = it->GetClient();
744 if (!cur_client) { // shouldn't happen
745 continue;
747 if (!queryitems.empty() && !queryitems.count(cur_client->ECID())) {
748 continue;
750 CValueMap *valuemap = NULL;
751 if (detail_level == EC_DETAIL_INC_UPDATE) {
752 valuemap = &tagmap.GetValueMap(cur_client->ECID());
754 CEC_UpDownClient_Tag cli_tag(cur_client, detail_level, valuemap);
756 response->AddTag(cli_tag);
759 return response;
763 static CECPacket *Get_EC_Response_GetDownloadQueue(const CECPacket *request, CFileEncoderMap &encoders)
765 CECPacket *response = new CECPacket(EC_OP_DLOAD_QUEUE);
767 EC_DETAIL_LEVEL detail_level = request->GetDetailLevel();
769 // request can contain list of queried items
770 CTagSet<uint32, EC_TAG_PARTFILE> queryitems(request);
772 encoders.UpdateEncoders();
774 for (unsigned int i = 0; i < theApp->downloadqueue->GetFileCount(); i++) {
775 CPartFile *cur_file = theApp->downloadqueue->GetFileByIndex(i);
777 if ( !queryitems.empty() && !queryitems.count(cur_file->ECID()) ) {
778 continue;
781 CEC_PartFile_Tag filetag(cur_file, detail_level);
783 CPartFile_Encoder * enc = static_cast<CPartFile_Encoder *>(encoders[cur_file->ECID()]);
784 if ( detail_level != EC_DETAIL_UPDATE ) {
785 enc->ResetEncoder();
787 enc->Encode(&filetag);
789 response->AddTag(filetag);
791 return response;
795 static CECPacket *Get_EC_Response_PartFile_Cmd(const CECPacket *request)
797 CECPacket *response = NULL;
799 // request can contain multiple files.
800 for (CECPacket::const_iterator it1 = request->begin(); it1 != request->end(); ++it1) {
801 const CECTag &hashtag = *it1;
803 wxASSERT(hashtag.GetTagName() == EC_TAG_PARTFILE);
805 CMD4Hash hash = hashtag.GetMD4Data();
806 CPartFile *pfile = theApp->downloadqueue->GetFileByID( hash );
808 if ( !pfile ) {
809 AddLogLineN(CFormat(_("Remote PartFile command failed: FileHash not found: %s")) % hash.Encode());
810 response = new CECPacket(EC_OP_FAILED);
811 response->AddTag(CECTag(EC_TAG_STRING, CFormat(wxString(wxTRANSLATE("FileHash not found: %s"))) % hash.Encode()));
812 //return response;
813 break;
815 switch (request->GetOpCode()) {
816 case EC_OP_PARTFILE_SWAP_A4AF_THIS:
817 CoreNotify_PartFile_Swap_A4AF(pfile);
818 break;
819 case EC_OP_PARTFILE_SWAP_A4AF_THIS_AUTO:
820 CoreNotify_PartFile_Swap_A4AF_Auto(pfile);
821 break;
822 case EC_OP_PARTFILE_SWAP_A4AF_OTHERS:
823 CoreNotify_PartFile_Swap_A4AF_Others(pfile);
824 break;
825 case EC_OP_PARTFILE_PAUSE:
826 pfile->PauseFile();
827 break;
828 case EC_OP_PARTFILE_RESUME:
829 pfile->ResumeFile();
830 pfile->SavePartFile();
831 break;
832 case EC_OP_PARTFILE_STOP:
833 pfile->StopFile();
834 break;
835 case EC_OP_PARTFILE_PRIO_SET: {
836 uint8 prio = hashtag.GetFirstTagSafe()->GetInt();
837 if ( prio == PR_AUTO ) {
838 pfile->SetAutoDownPriority(1);
839 } else {
840 pfile->SetAutoDownPriority(0);
841 pfile->SetDownPriority(prio);
844 break;
845 case EC_OP_PARTFILE_DELETE:
846 if ( thePrefs::StartNextFile() && (pfile->GetStatus() != PS_PAUSED) ) {
847 theApp->downloadqueue->StartNextFile(pfile);
849 pfile->Delete();
850 break;
852 case EC_OP_PARTFILE_SET_CAT:
853 pfile->SetCategory(hashtag.GetFirstTagSafe()->GetInt());
854 break;
856 default:
857 response = new CECPacket(EC_OP_FAILED);
858 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("OOPS! OpCode processing error!")));
859 break;
862 if (!response) {
863 response = new CECPacket(EC_OP_NOOP);
865 return response;
868 static CECPacket *Get_EC_Response_Server_Add(const CECPacket *request)
870 CECPacket *response = NULL;
872 wxString full_addr = request->GetTagByNameSafe(EC_TAG_SERVER_ADDRESS)->GetStringData();
873 wxString name = request->GetTagByNameSafe(EC_TAG_SERVER_NAME)->GetStringData();
875 wxString s_ip = full_addr.Left(full_addr.Find(':'));
876 wxString s_port = full_addr.Mid(full_addr.Find(':') + 1);
878 long port = StrToULong(s_port);
879 CServer* toadd = new CServer(port, s_ip);
880 toadd->SetListName(name.IsEmpty() ? full_addr : name);
882 if ( theApp->AddServer(toadd, true) ) {
883 response = new CECPacket(EC_OP_NOOP);
884 } else {
885 response = new CECPacket(EC_OP_FAILED);
886 response->AddTag(CECTag(EC_TAG_STRING, _("Server not added")));
887 delete toadd;
890 return response;
893 static CECPacket *Get_EC_Response_Server(const CECPacket *request)
895 CECPacket *response = NULL;
896 const CECTag *srv_tag = request->GetTagByName(EC_TAG_SERVER);
897 CServer *srv = 0;
898 if ( srv_tag ) {
899 srv = theApp->serverlist->GetServerByIPTCP(srv_tag->GetIPv4Data().IP(), srv_tag->GetIPv4Data().m_port);
900 // server tag passed, but server not found
901 if ( !srv ) {
902 response = new CECPacket(EC_OP_FAILED);
903 response->AddTag(CECTag(EC_TAG_STRING,
904 CFormat(wxString(wxTRANSLATE("server not found: %s"))) % srv_tag->GetIPv4Data().StringIP()));
905 return response;
908 switch (request->GetOpCode()) {
909 case EC_OP_SERVER_DISCONNECT:
910 theApp->serverconnect->Disconnect();
911 response = new CECPacket(EC_OP_NOOP);
912 break;
913 case EC_OP_SERVER_REMOVE:
914 if ( srv ) {
915 theApp->serverlist->RemoveServer(srv);
916 response = new CECPacket(EC_OP_NOOP);
917 } else {
918 response = new CECPacket(EC_OP_FAILED);
919 response->AddTag(CECTag(EC_TAG_STRING,
920 wxTRANSLATE("need to define server to be removed")));
922 break;
923 case EC_OP_SERVER_CONNECT:
924 if (thePrefs::GetNetworkED2K()) {
925 if ( srv ) {
926 theApp->serverconnect->ConnectToServer(srv);
927 response = new CECPacket(EC_OP_NOOP);
928 } else {
929 theApp->serverconnect->ConnectToAnyServer();
930 response = new CECPacket(EC_OP_NOOP);
932 } else {
933 response = new CECPacket(EC_OP_FAILED);
934 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("eD2k is disabled in preferences.")));
936 break;
938 if (!response) {
939 response = new CECPacket(EC_OP_FAILED);
940 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("OOPS! OpCode processing error!")));
942 return response;
946 static CECPacket *Get_EC_Response_Friend(const CECPacket *request)
948 CECPacket *response = NULL;
949 const CECTag *tag = request->GetTagByName(EC_TAG_FRIEND_ADD);
950 if (tag) {
951 const CECTag *subtag = tag->GetTagByName(EC_TAG_CLIENT);
952 if (subtag) {
953 CUpDownClient * client = theApp->clientlist->FindClientByECID(subtag->GetInt());
954 if (client) {
955 theApp->friendlist->AddFriend(CCLIENTREF(client, wxT("Get_EC_Response_Friend theApp->friendlist->AddFriend")));
956 response = new CECPacket(EC_OP_NOOP);
958 } else {
959 const CECTag *hashtag = tag->GetTagByName(EC_TAG_FRIEND_HASH);
960 const CECTag *iptag = tag->GetTagByName(EC_TAG_FRIEND_IP);
961 const CECTag *porttag = tag->GetTagByName(EC_TAG_FRIEND_PORT);
962 const CECTag *nametag = tag->GetTagByName(EC_TAG_FRIEND_NAME);
963 if (hashtag && iptag && porttag && nametag) {
964 theApp->friendlist->AddFriend(hashtag->GetMD4Data(), iptag->GetInt(), porttag->GetInt(), nametag->GetStringData());
965 response = new CECPacket(EC_OP_NOOP);
968 } else if ((tag = request->GetTagByName(EC_TAG_FRIEND_REMOVE))) {
969 const CECTag *subtag = tag->GetTagByName(EC_TAG_FRIEND);
970 if (subtag) {
971 CFriend * Friend = theApp->friendlist->FindFriend(subtag->GetInt());
972 if (Friend) {
973 theApp->friendlist->RemoveFriend(Friend);
974 response = new CECPacket(EC_OP_NOOP);
977 } else if ((tag = request->GetTagByName(EC_TAG_FRIEND_FRIENDSLOT))) {
978 const CECTag *subtag = tag->GetTagByName(EC_TAG_FRIEND);
979 if (subtag) {
980 CFriend * Friend = theApp->friendlist->FindFriend(subtag->GetInt());
981 if (Friend) {
982 theApp->friendlist->SetFriendSlot(Friend, tag->GetInt() != 0);
983 response = new CECPacket(EC_OP_NOOP);
986 } else if ((tag = request->GetTagByName(EC_TAG_FRIEND_SHARED))) {
987 response = new CECPacket(EC_OP_FAILED);
988 response->AddTag(CECTag(EC_TAG_STRING, wxT("Request shared files list not implemented yet.")));
989 #if 0
990 // This works fine - but there is no way atm to transfer the results to amulegui, so disable it for now.
992 const CECTag *subtag = tag->GetTagByName(EC_TAG_FRIEND);
993 if (subtag) {
994 CFriend * Friend = theApp->friendlist->FindFriend(subtag->GetInt());
995 if (Friend) {
996 theApp->friendlist->RequestSharedFileList(Friend);
997 response = new CECPacket(EC_OP_NOOP);
999 } else if ((subtag = tag->GetTagByName(EC_TAG_CLIENT))) {
1000 CUpDownClient * client = theApp->clientlist->FindClientByECID(subtag->GetInt());
1001 if (client) {
1002 client->RequestSharedFileList();
1003 response = new CECPacket(EC_OP_NOOP);
1006 #endif
1009 if (!response) {
1010 response = new CECPacket(EC_OP_FAILED);
1011 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("OOPS! OpCode processing error!")));
1013 return response;
1017 static CECPacket *Get_EC_Response_Search_Results(const CECPacket *request)
1019 CECPacket *response = new CECPacket(EC_OP_SEARCH_RESULTS);
1021 EC_DETAIL_LEVEL detail_level = request->GetDetailLevel();
1023 // request can contain list of queried items
1024 CTagSet<uint32, EC_TAG_SEARCHFILE> queryitems(request);
1026 const CSearchResultList& list = theApp->searchlist->GetSearchResults(0xffffffff);
1027 CSearchResultList::const_iterator it = list.begin();
1028 while (it != list.end()) {
1029 CSearchFile* sf = *it++;
1030 if ( !queryitems.empty() && !queryitems.count(sf->ECID()) ) {
1031 continue;
1033 response->AddTag(CEC_SearchFile_Tag(sf, detail_level));
1035 return response;
1038 static CECPacket *Get_EC_Response_Search_Results(CObjTagMap &tagmap)
1040 CECPacket *response = new CECPacket(EC_OP_SEARCH_RESULTS);
1042 const CSearchResultList& list = theApp->searchlist->GetSearchResults(0xffffffff);
1043 CSearchResultList::const_iterator it = list.begin();
1044 while (it != list.end()) {
1045 CSearchFile* sf = *it++;
1046 CValueMap &valuemap = tagmap.GetValueMap(sf->ECID());
1047 response->AddTag(CEC_SearchFile_Tag(sf, EC_DETAIL_INC_UPDATE, &valuemap));
1048 // Add the children
1049 if (sf->HasChildren()) {
1050 const CSearchResultList& children = sf->GetChildren();
1051 for (size_t i = 0; i < children.size(); ++i) {
1052 CSearchFile* sfc = children.at(i);
1053 CValueMap &valuemap1 = tagmap.GetValueMap(sfc->ECID());
1054 response->AddTag(CEC_SearchFile_Tag(sfc, EC_DETAIL_INC_UPDATE, &valuemap1));
1058 return response;
1061 static CECPacket *Get_EC_Response_Search_Results_Download(const CECPacket *request)
1063 CECPacket *response = new CECPacket(EC_OP_STRINGS);
1064 for (CECPacket::const_iterator it = request->begin(); it != request->end(); ++it) {
1065 const CECTag &tag = *it;
1066 CMD4Hash hash = tag.GetMD4Data();
1067 uint8 category = tag.GetFirstTagSafe()->GetInt();
1068 theApp->searchlist->AddFileToDownloadByHash(hash, category);
1070 return response;
1073 static CECPacket *Get_EC_Response_Search_Stop(const CECPacket *WXUNUSED(request))
1075 CECPacket *reply = new CECPacket(EC_OP_MISC_DATA);
1076 theApp->searchlist->StopSearch();
1077 return reply;
1080 static CECPacket *Get_EC_Response_Search(const CECPacket *request)
1082 wxString response;
1084 const CEC_Search_Tag *search_request = static_cast<const CEC_Search_Tag *>(request->GetFirstTagSafe());
1085 theApp->searchlist->RemoveResults(0xffffffff);
1087 CSearchList::CSearchParams params;
1088 params.searchString = search_request->SearchText();
1089 params.typeText = search_request->SearchFileType();
1090 params.extension = search_request->SearchExt();
1091 params.minSize = search_request->MinSize();
1092 params.maxSize = search_request->MaxSize();
1093 params.availability = search_request->Avail();
1096 EC_SEARCH_TYPE search_type = search_request->SearchType();
1097 SearchType core_search_type = LocalSearch;
1098 uint32 op = EC_OP_FAILED;
1099 switch (search_type) {
1100 case EC_SEARCH_GLOBAL:
1101 core_search_type = GlobalSearch;
1102 /* fall through */
1103 case EC_SEARCH_KAD:
1104 if (core_search_type != GlobalSearch) { // Not a global search obviously
1105 core_search_type = KadSearch;
1107 /* fall through */
1108 case EC_SEARCH_LOCAL: {
1109 uint32 search_id = 0xffffffff;
1110 wxString error = theApp->searchlist->StartNewSearch(&search_id, core_search_type, params);
1111 if (!error.IsEmpty()) {
1112 response = error;
1113 } else {
1114 response = wxTRANSLATE("Search in progress. Refetch results in a moment!");
1115 op = EC_OP_STRINGS;
1117 break;
1119 case EC_SEARCH_WEB:
1120 response = wxTRANSLATE("WebSearch from remote interface makes no sense.");
1121 break;
1124 CECPacket *reply = new CECPacket(op);
1125 // error or search in progress
1126 reply->AddTag(CECTag(EC_TAG_STRING, response));
1128 return reply;
1131 static CECPacket *Get_EC_Response_Set_SharedFile_Prio(const CECPacket *request)
1133 CECPacket *response = new CECPacket(EC_OP_NOOP);
1134 for (CECPacket::const_iterator it = request->begin(); it != request->end(); ++it) {
1135 const CECTag &tag = *it;
1136 CMD4Hash hash = tag.GetMD4Data();
1137 uint8 prio = tag.GetFirstTagSafe()->GetInt();
1138 CKnownFile* cur_file = theApp->sharedfiles->GetFileByID(hash);
1139 if ( !cur_file ) {
1140 continue;
1142 if (prio == PR_AUTO) {
1143 cur_file->SetAutoUpPriority(1);
1144 cur_file->UpdateAutoUpPriority();
1145 } else {
1146 cur_file->SetAutoUpPriority(0);
1147 cur_file->SetUpPriority(prio);
1149 Notify_SharedFilesUpdateItem(cur_file);
1152 return response;
1155 void CPartFile_Encoder::Encode(CECTag *parent)
1158 // Source part frequencies
1160 CKnownFile_Encoder::Encode(parent);
1163 // Gaps
1165 const CGapList& gaplist = m_PartFile()->GetGapList();
1166 const size_t gap_list_size = gaplist.size();
1167 ArrayOfUInts64 gaps;
1168 gaps.reserve(gap_list_size * 2);
1170 for (CGapList::const_iterator curr_pos = gaplist.begin();
1171 curr_pos != gaplist.end(); ++curr_pos) {
1172 gaps.push_back(curr_pos.start());
1173 gaps.push_back(curr_pos.end());
1176 int gap_enc_size = 0;
1177 bool changed;
1178 const uint8 *gap_enc_data = m_gap_status.Encode(gaps, gap_enc_size, changed);
1179 if (changed) {
1180 parent->AddTag(CECTag(EC_TAG_PARTFILE_GAP_STATUS, gap_enc_size, (void *)gap_enc_data));
1182 delete[] gap_enc_data;
1185 // Requested blocks
1187 ArrayOfUInts64 req_buffer;
1188 const CPartFile::CReqBlockPtrList& requestedblocks = m_PartFile()->GetRequestedBlockList();
1189 CPartFile::CReqBlockPtrList::const_iterator curr_pos2 = requestedblocks.begin();
1191 for ( ; curr_pos2 != requestedblocks.end(); ++curr_pos2 ) {
1192 Requested_Block_Struct* block = *curr_pos2;
1193 req_buffer.push_back(block->StartOffset);
1194 req_buffer.push_back(block->EndOffset);
1196 int req_enc_size = 0;
1197 const uint8 *req_enc_data = m_req_status.Encode(req_buffer, req_enc_size, changed);
1198 if (changed) {
1199 parent->AddTag(CECTag(EC_TAG_PARTFILE_REQ_STATUS, req_enc_size, (void *)req_enc_data));
1201 delete[] req_enc_data;
1204 // Source names
1206 // First count occurrence of all source names
1208 CECEmptyTag sourceNames(EC_TAG_PARTFILE_SOURCE_NAMES);
1209 typedef std::map<wxString, int> strIntMap;
1210 strIntMap nameMap;
1211 const CPartFile::SourceSet &sources = m_PartFile()->GetSourceList();
1212 for (CPartFile::SourceSet::const_iterator it = sources.begin(); it != sources.end(); ++it) {
1213 const CClientRef &cur_src = *it;
1214 if (cur_src.GetRequestFile() != m_file || cur_src.GetClientFilename().Length() == 0) {
1215 continue;
1217 const wxString &name = cur_src.GetClientFilename();
1218 strIntMap::iterator itm = nameMap.find(name);
1219 if (itm == nameMap.end()) {
1220 nameMap[name] = 1;
1221 } else {
1222 itm->second++;
1226 // Go through our last list
1228 for (SourcenameItemMap::iterator it1 = m_sourcenameItemMap.begin(); it1 != m_sourcenameItemMap.end();) {
1229 SourcenameItemMap::iterator it2 = it1++;
1230 strIntMap::iterator itm = nameMap.find(it2->second.name);
1231 if (itm == nameMap.end()) {
1232 // name doesn't exist anymore, tell client to forget it
1233 CECTag tag(EC_TAG_PARTFILE_SOURCE_NAMES, it2->first);
1234 tag.AddTag(CECIntTag(EC_TAG_PARTFILE_SOURCE_NAMES_COUNTS, 0));
1235 sourceNames.AddTag(tag);
1236 // and forget it
1237 m_sourcenameItemMap.erase(it2);
1238 } else {
1239 // update count if it changed
1240 if (it2->second.count != itm->second) {
1241 CECTag tag(EC_TAG_PARTFILE_SOURCE_NAMES, it2->first);
1242 tag.AddTag(CECIntTag(EC_TAG_PARTFILE_SOURCE_NAMES_COUNTS, itm->second));
1243 sourceNames.AddTag(tag);
1244 it2->second.count = itm->second;
1246 // remove it from nameMap so that only new names are left there
1247 nameMap.erase(itm);
1251 // Add new names
1253 for (strIntMap::iterator it3 = nameMap.begin(); it3 != nameMap.end(); ++it3) {
1254 int id = ++m_sourcenameID;
1255 CECIntTag tag(EC_TAG_PARTFILE_SOURCE_NAMES, id);
1256 tag.AddTag(CECTag(EC_TAG_PARTFILE_SOURCE_NAMES, it3->first));
1257 tag.AddTag(CECIntTag(EC_TAG_PARTFILE_SOURCE_NAMES_COUNTS, it3->second));
1258 sourceNames.AddTag(tag);
1259 // remember it
1260 m_sourcenameItemMap[id] = SourcenameItem(it3->first, it3->second);
1262 if (sourceNames.HasChildTags()) {
1263 parent->AddTag(sourceNames);
1268 void CPartFile_Encoder::ResetEncoder()
1270 CKnownFile_Encoder::ResetEncoder();
1271 m_gap_status.ResetEncoder();
1272 m_req_status.ResetEncoder();
1275 void CKnownFile_Encoder::Encode(CECTag *parent)
1278 // Source part frequencies
1280 // Reference to the availability list
1281 const ArrayOfUInts16& list = m_file->IsPartFile() ?
1282 static_cast<const CPartFile*>(m_file)->m_SrcpartFrequency :
1283 m_file->m_AvailPartFrequency;
1284 // Don't add tag if available parts aren't populated yet.
1285 if (!list.empty()) {
1286 int part_enc_size;
1287 bool changed;
1288 const uint8 *part_enc_data = m_enc_data.Encode(list, part_enc_size, changed);
1289 if (changed) {
1290 parent->AddTag(CECTag(EC_TAG_PARTFILE_PART_STATUS, part_enc_size, part_enc_data));
1292 delete[] part_enc_data;
1296 static CECPacket *GetStatsGraphs(const CECPacket *request)
1298 CECPacket *response = NULL;
1300 switch (request->GetDetailLevel()) {
1301 case EC_DETAIL_WEB:
1302 case EC_DETAIL_FULL: {
1303 double dTimestamp = 0.0;
1304 if (request->GetTagByName(EC_TAG_STATSGRAPH_LAST) != NULL) {
1305 dTimestamp = request->GetTagByName(EC_TAG_STATSGRAPH_LAST)->GetDoubleData();
1307 uint16 nScale = request->GetTagByNameSafe(EC_TAG_STATSGRAPH_SCALE)->GetInt();
1308 uint16 nMaxPoints = request->GetTagByNameSafe(EC_TAG_STATSGRAPH_WIDTH)->GetInt();
1309 uint32 *graphData;
1310 unsigned int numPoints = theApp->m_statistics->GetHistoryForWeb(nMaxPoints, (double)nScale, &dTimestamp, &graphData);
1311 if (numPoints) {
1312 response = new CECPacket(EC_OP_STATSGRAPHS);
1313 response->AddTag(CECTag(EC_TAG_STATSGRAPH_DATA, 4 * numPoints * sizeof(uint32), graphData));
1314 delete [] graphData;
1315 response->AddTag(CECTag(EC_TAG_STATSGRAPH_LAST, dTimestamp));
1316 } else {
1317 response = new CECPacket(EC_OP_FAILED);
1318 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("No points for graph.")));
1320 break;
1322 case EC_DETAIL_INC_UPDATE:
1323 case EC_DETAIL_UPDATE:
1324 case EC_DETAIL_CMD:
1325 // No graphs
1326 response = new CECPacket(EC_OP_FAILED);
1327 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Your client is not configured for this detail level.")));
1328 break;
1330 if (!response) {
1331 response = new CECPacket(EC_OP_FAILED);
1332 // Unknown reason
1335 return response;
1338 CECPacket *CECServerSocket::ProcessRequest2(const CECPacket *request)
1341 if ( !request ) {
1342 return 0;
1345 CECPacket *response = NULL;
1347 switch (request->GetOpCode()) {
1349 // Misc commands
1351 case EC_OP_SHUTDOWN:
1352 if (!theApp->IsOnShutDown()) {
1353 response = new CECPacket(EC_OP_NOOP);
1354 AddLogLineC(_("External Connection: shutdown requested"));
1355 #ifndef AMULE_DAEMON
1357 wxCloseEvent evt;
1358 evt.SetCanVeto(false);
1359 theApp->ShutDown(evt);
1361 #else
1362 theApp->ExitMainLoop();
1363 #endif
1364 } else {
1365 response = new CECPacket(EC_OP_FAILED);
1366 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Already shutting down.")));
1368 break;
1369 case EC_OP_ADD_LINK:
1370 for (CECPacket::const_iterator it = request->begin(); it != request->end(); ++it) {
1371 const CECTag &tag = *it;
1372 wxString link = tag.GetStringData();
1373 int category = 0;
1374 const CECTag *cattag = tag.GetTagByName(EC_TAG_PARTFILE_CAT);
1375 if (cattag) {
1376 category = cattag->GetInt();
1378 AddLogLineC(CFormat(_("ExternalConn: adding link '%s'.")) % link);
1379 if (response) {
1380 delete response;
1382 if ( theApp->downloadqueue->AddLink(link, category) ) {
1383 response = new CECPacket(EC_OP_NOOP);
1384 } else {
1385 // Error messages are printed by the add function.
1386 response = new CECPacket(EC_OP_FAILED);
1387 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Invalid link or already on list.")));
1390 break;
1392 // Status requests
1394 case EC_OP_STAT_REQ:
1395 response = Get_EC_Response_StatRequest(request, m_LoggerAccess);
1396 response->AddTag(CEC_ConnState_Tag(request->GetDetailLevel()));
1397 break;
1398 case EC_OP_GET_CONNSTATE:
1399 response = new CECPacket(EC_OP_MISC_DATA);
1400 response->AddTag(CEC_ConnState_Tag(request->GetDetailLevel()));
1401 break;
1405 case EC_OP_GET_SHARED_FILES:
1406 if ( request->GetDetailLevel() != EC_DETAIL_INC_UPDATE ) {
1407 response = Get_EC_Response_GetSharedFiles(request, m_FileEncoder);
1409 break;
1410 case EC_OP_GET_DLOAD_QUEUE:
1411 if ( request->GetDetailLevel() != EC_DETAIL_INC_UPDATE ) {
1412 response = Get_EC_Response_GetDownloadQueue(request, m_FileEncoder);
1414 break;
1416 // This will evolve into an update-all for inc tags
1418 case EC_OP_GET_UPDATE:
1419 if ( request->GetDetailLevel() == EC_DETAIL_INC_UPDATE ) {
1420 response = Get_EC_Response_GetUpdate(m_FileEncoder, m_obj_tagmap);
1422 break;
1423 case EC_OP_GET_ULOAD_QUEUE:
1424 response = Get_EC_Response_GetClientQueue(request, m_obj_tagmap, EC_OP_ULOAD_QUEUE);
1425 break;
1426 case EC_OP_PARTFILE_SWAP_A4AF_THIS:
1427 case EC_OP_PARTFILE_SWAP_A4AF_THIS_AUTO:
1428 case EC_OP_PARTFILE_SWAP_A4AF_OTHERS:
1429 case EC_OP_PARTFILE_PAUSE:
1430 case EC_OP_PARTFILE_RESUME:
1431 case EC_OP_PARTFILE_STOP:
1432 case EC_OP_PARTFILE_PRIO_SET:
1433 case EC_OP_PARTFILE_DELETE:
1434 case EC_OP_PARTFILE_SET_CAT:
1435 response = Get_EC_Response_PartFile_Cmd(request);
1436 break;
1437 case EC_OP_SHAREDFILES_RELOAD:
1438 theApp->sharedfiles->Reload();
1439 response = new CECPacket(EC_OP_NOOP);
1440 break;
1441 case EC_OP_SHARED_SET_PRIO:
1442 response = Get_EC_Response_Set_SharedFile_Prio(request);
1443 break;
1444 case EC_OP_RENAME_FILE: {
1445 CMD4Hash fileHash = request->GetTagByNameSafe(EC_TAG_KNOWNFILE)->GetMD4Data();
1446 wxString newName = request->GetTagByNameSafe(EC_TAG_PARTFILE_NAME)->GetStringData();
1447 // search first in downloadqueue - it might be in known files as well
1448 CKnownFile* file = theApp->downloadqueue->GetFileByID(fileHash);
1449 if (!file) {
1450 file = theApp->knownfiles->FindKnownFileByID(fileHash);
1452 if (!file) {
1453 response = new CECPacket(EC_OP_FAILED);
1454 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("File not found.")));
1455 break;
1457 if (newName.IsEmpty()) {
1458 response = new CECPacket(EC_OP_FAILED);
1459 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Invalid file name.")));
1460 break;
1463 if (theApp->sharedfiles->RenameFile(file, CPath(newName))) {
1464 response = new CECPacket(EC_OP_NOOP);
1465 } else {
1466 response = new CECPacket(EC_OP_FAILED);
1467 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Unable to rename file.")));
1470 break;
1472 case EC_OP_CLEAR_COMPLETED: {
1473 ListOfUInts32 toClear;
1474 for (CECTag::const_iterator it = request->begin(); it != request->end(); ++it) {
1475 if (it->GetTagName() == EC_TAG_ECID) {
1476 toClear.push_back(it->GetInt());
1479 theApp->downloadqueue->ClearCompleted(toClear);
1480 response = new CECPacket(EC_OP_NOOP);
1481 break;
1483 case EC_OP_CLIENT_SWAP_TO_ANOTHER_FILE: {
1484 theApp->sharedfiles->Reload();
1485 uint32 idClient = request->GetTagByNameSafe(EC_TAG_CLIENT)->GetInt();
1486 CUpDownClient * client = theApp->clientlist->FindClientByECID(idClient);
1487 CMD4Hash idFile = request->GetTagByNameSafe(EC_TAG_PARTFILE)->GetMD4Data();
1488 CPartFile * file = theApp->downloadqueue->GetFileByID(idFile);
1489 if (client && file) {
1490 client->SwapToAnotherFile( true, false, false, file);
1492 response = new CECPacket(EC_OP_NOOP);
1493 break;
1495 case EC_OP_SHARED_FILE_SET_COMMENT: {
1496 CMD4Hash hash = request->GetTagByNameSafe(EC_TAG_KNOWNFILE)->GetMD4Data();
1497 CKnownFile * file = theApp->sharedfiles->GetFileByID(hash);
1498 if (file) {
1499 wxString newComment = request->GetTagByNameSafe(EC_TAG_KNOWNFILE_COMMENT)->GetStringData();
1500 uint8 newRating = request->GetTagByNameSafe(EC_TAG_KNOWNFILE_RATING)->GetInt();
1501 CoreNotify_KnownFile_Comment_Set(file, newComment, newRating);
1503 response = new CECPacket(EC_OP_NOOP);
1504 break;
1508 // Server commands
1510 case EC_OP_SERVER_ADD:
1511 response = Get_EC_Response_Server_Add(request);
1512 break;
1513 case EC_OP_SERVER_DISCONNECT:
1514 case EC_OP_SERVER_CONNECT:
1515 case EC_OP_SERVER_REMOVE:
1516 response = Get_EC_Response_Server(request);
1517 break;
1518 case EC_OP_GET_SERVER_LIST: {
1519 response = new CECPacket(EC_OP_SERVER_LIST);
1520 if (!thePrefs::GetNetworkED2K()) {
1521 // Kad only: just send an empty list
1522 break;
1524 EC_DETAIL_LEVEL detail_level = request->GetDetailLevel();
1525 std::vector<const CServer*> servers = theApp->serverlist->CopySnapshot();
1526 for (
1527 std::vector<const CServer*>::const_iterator it = servers.begin();
1528 it != servers.end();
1529 ++it
1531 response->AddTag(CEC_Server_Tag(*it, detail_level));
1534 break;
1535 case EC_OP_SERVER_UPDATE_FROM_URL: {
1536 wxString url = request->GetFirstTagSafe()->GetStringData();
1538 // Save the new url, and update the UI (if not amuled).
1539 Notify_ServersURLChanged(url);
1540 thePrefs::SetEd2kServersUrl(url);
1542 theApp->serverlist->UpdateServerMetFromURL(url);
1543 response = new CECPacket(EC_OP_NOOP);
1544 break;
1546 case EC_OP_SERVER_SET_STATIC_PRIO: {
1547 uint32 ecid = request->GetTagByNameSafe(EC_TAG_SERVER)->GetInt();
1548 CServer * server = theApp->serverlist->GetServerByECID(ecid);
1549 if (server) {
1550 const CECTag * staticTag = request->GetTagByName(EC_TAG_SERVER_STATIC);
1551 if (staticTag) {
1552 theApp->serverlist->SetStaticServer(server, staticTag->GetInt() > 0);
1554 const CECTag * prioTag = request->GetTagByName(EC_TAG_SERVER_PRIO);
1555 if (prioTag) {
1556 theApp->serverlist->SetServerPrio(server, prioTag->GetInt());
1559 response = new CECPacket(EC_OP_NOOP);
1560 break;
1563 // Friends
1565 case EC_OP_FRIEND:
1566 response = Get_EC_Response_Friend(request);
1567 break;
1570 // IPFilter
1572 case EC_OP_IPFILTER_RELOAD:
1573 NotifyAlways_IPFilter_Reload();
1574 response = new CECPacket(EC_OP_NOOP);
1575 break;
1577 case EC_OP_IPFILTER_UPDATE: {
1578 wxString url = request->GetFirstTagSafe()->GetStringData();
1579 if (url.IsEmpty()) {
1580 url = thePrefs::IPFilterURL();
1582 NotifyAlways_IPFilter_Update(url);
1583 response = new CECPacket(EC_OP_NOOP);
1584 break;
1587 // Search
1589 case EC_OP_SEARCH_START:
1590 response = Get_EC_Response_Search(request);
1591 break;
1593 case EC_OP_SEARCH_STOP:
1594 response = Get_EC_Response_Search_Stop(request);
1595 break;
1597 case EC_OP_SEARCH_RESULTS:
1598 if ( request->GetDetailLevel() == EC_DETAIL_INC_UPDATE ) {
1599 response = Get_EC_Response_Search_Results(m_obj_tagmap);
1600 } else {
1601 response = Get_EC_Response_Search_Results(request);
1603 break;
1605 case EC_OP_SEARCH_PROGRESS:
1606 response = new CECPacket(EC_OP_SEARCH_PROGRESS);
1607 response->AddTag(CECTag(EC_TAG_SEARCH_STATUS,
1608 theApp->searchlist->GetSearchProgress()));
1609 break;
1611 case EC_OP_DOWNLOAD_SEARCH_RESULT:
1612 response = Get_EC_Response_Search_Results_Download(request);
1613 break;
1615 // Preferences
1617 case EC_OP_GET_PREFERENCES:
1618 response = new CEC_Prefs_Packet(request->GetTagByNameSafe(EC_TAG_SELECT_PREFS)->GetInt(), request->GetDetailLevel());
1619 break;
1620 case EC_OP_SET_PREFERENCES:
1621 static_cast<const CEC_Prefs_Packet *>(request)->Apply();
1622 theApp->glob_prefs->Save();
1623 if (thePrefs::IsFilteringClients()) {
1624 theApp->clientlist->FilterQueues();
1626 if (thePrefs::IsFilteringServers()) {
1627 theApp->serverlist->FilterServers();
1629 if (!thePrefs::GetNetworkED2K() && theApp->IsConnectedED2K()) {
1630 theApp->DisconnectED2K();
1632 if (!thePrefs::GetNetworkKademlia() && theApp->IsConnectedKad()) {
1633 theApp->StopKad();
1635 response = new CECPacket(EC_OP_NOOP);
1636 break;
1638 case EC_OP_CREATE_CATEGORY:
1639 if ( request->GetTagCount() == 1 ) {
1640 CEC_Category_Tag tag(*static_cast<const CEC_Category_Tag*>(request->GetFirstTagSafe()));
1641 if (tag.Create()) {
1642 response = new CECPacket(EC_OP_NOOP);
1643 } else {
1644 response = new CECPacket(EC_OP_FAILED);
1645 response->AddTag(CECTag(EC_TAG_CATEGORY, theApp->glob_prefs->GetCatCount() - 1));
1646 response->AddTag(CECTag(EC_TAG_CATEGORY_PATH, tag.Path()));
1648 Notify_CategoryAdded();
1649 } else {
1650 response = new CECPacket(EC_OP_NOOP);
1652 break;
1653 case EC_OP_UPDATE_CATEGORY:
1654 if ( request->GetTagCount() == 1 ) {
1655 CEC_Category_Tag tag(*static_cast<const CEC_Category_Tag*>(request->GetFirstTagSafe()));
1656 if (tag.Apply()) {
1657 response = new CECPacket(EC_OP_NOOP);
1658 } else {
1659 response = new CECPacket(EC_OP_FAILED);
1660 response->AddTag(CECTag(EC_TAG_CATEGORY, tag.GetInt()));
1661 response->AddTag(CECTag(EC_TAG_CATEGORY_PATH, tag.Path()));
1663 Notify_CategoryUpdate(tag.GetInt());
1664 } else {
1665 response = new CECPacket(EC_OP_NOOP);
1667 break;
1668 case EC_OP_DELETE_CATEGORY:
1669 if ( request->GetTagCount() == 1 ) {
1670 uint32 cat = request->GetFirstTagSafe()->GetInt();
1671 // this noes not only update the gui, but actually deletes the cat
1672 Notify_CategoryDelete(cat);
1674 response = new CECPacket(EC_OP_NOOP);
1675 break;
1678 // Logging
1680 case EC_OP_ADDLOGLINE:
1681 // cppcheck-suppress duplicateBranch
1682 if (request->GetTagByName(EC_TAG_LOG_TO_STATUS) != NULL) {
1683 AddLogLineC(request->GetTagByNameSafe(EC_TAG_STRING)->GetStringData());
1684 } else {
1685 AddLogLineN(request->GetTagByNameSafe(EC_TAG_STRING)->GetStringData());
1687 response = new CECPacket(EC_OP_NOOP);
1688 break;
1689 case EC_OP_ADDDEBUGLOGLINE:
1690 // cppcheck-suppress duplicateBranch
1691 if (request->GetTagByName(EC_TAG_LOG_TO_STATUS) != NULL) {
1692 AddDebugLogLineC(logGeneral, request->GetTagByNameSafe(EC_TAG_STRING)->GetStringData());
1693 } else {
1694 AddDebugLogLineN(logGeneral, request->GetTagByNameSafe(EC_TAG_STRING)->GetStringData());
1696 response = new CECPacket(EC_OP_NOOP);
1697 break;
1698 case EC_OP_GET_LOG:
1699 response = new CECPacket(EC_OP_LOG);
1700 response->AddTag(CECTag(EC_TAG_STRING, theApp->GetLog(false)));
1701 break;
1702 case EC_OP_GET_DEBUGLOG:
1703 response = new CECPacket(EC_OP_DEBUGLOG);
1704 response->AddTag(CECTag(EC_TAG_STRING, theApp->GetDebugLog(false)));
1705 break;
1706 case EC_OP_RESET_LOG:
1707 theApp->GetLog(true);
1708 response = new CECPacket(EC_OP_NOOP);
1709 break;
1710 case EC_OP_RESET_DEBUGLOG:
1711 theApp->GetDebugLog(true);
1712 response = new CECPacket(EC_OP_NOOP);
1713 break;
1714 case EC_OP_GET_LAST_LOG_ENTRY:
1716 wxString tmp = theApp->GetLog(false);
1717 if (tmp.Last() == '\n') {
1718 tmp.RemoveLast();
1720 response = new CECPacket(EC_OP_LOG);
1721 response->AddTag(CECTag(EC_TAG_STRING, tmp.AfterLast('\n')));
1723 break;
1724 case EC_OP_GET_SERVERINFO:
1725 response = new CECPacket(EC_OP_SERVERINFO);
1726 response->AddTag(CECTag(EC_TAG_STRING, theApp->GetServerLog(false)));
1727 break;
1728 case EC_OP_CLEAR_SERVERINFO:
1729 theApp->GetServerLog(true);
1730 response = new CECPacket(EC_OP_NOOP);
1731 break;
1733 // Statistics
1735 case EC_OP_GET_STATSGRAPHS:
1736 response = GetStatsGraphs(request);
1737 break;
1738 case EC_OP_GET_STATSTREE: {
1739 theApp->m_statistics->UpdateStatsTree();
1740 response = new CECPacket(EC_OP_STATSTREE);
1741 CECTag* tree = theStats::GetECStatTree(request->GetTagByNameSafe(EC_TAG_STATTREE_CAPPING)->GetInt());
1742 if (tree) {
1743 response->AddTag(*tree);
1744 delete tree;
1746 if (request->GetDetailLevel() == EC_DETAIL_WEB) {
1747 response->AddTag(CECTag(EC_TAG_SERVER_VERSION, wxT(VERSION)));
1748 response->AddTag(CECTag(EC_TAG_USER_NICK, thePrefs::GetUserNick()));
1750 break;
1754 // Kad
1756 case EC_OP_KAD_START:
1757 if (thePrefs::GetNetworkKademlia()) {
1758 response = new CECPacket(EC_OP_NOOP);
1759 if ( !Kademlia::CKademlia::IsRunning() ) {
1760 Kademlia::CKademlia::Start();
1761 theApp->ShowConnectionState();
1763 } else {
1764 response = new CECPacket(EC_OP_FAILED);
1765 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Kad is disabled in preferences.")));
1767 break;
1768 case EC_OP_KAD_STOP:
1769 theApp->StopKad();
1770 theApp->ShowConnectionState();
1771 response = new CECPacket(EC_OP_NOOP);
1772 break;
1773 case EC_OP_KAD_UPDATE_FROM_URL: {
1774 wxString url = request->GetFirstTagSafe()->GetStringData();
1776 // Save the new url, and update the UI (if not amuled).
1777 Notify_NodesURLChanged(url);
1778 thePrefs::SetKadNodesUrl(url);
1780 theApp->UpdateNotesDat(url);
1781 response = new CECPacket(EC_OP_NOOP);
1782 break;
1784 case EC_OP_KAD_BOOTSTRAP_FROM_IP:
1785 if (thePrefs::GetNetworkKademlia()) {
1786 theApp->BootstrapKad(request->GetTagByNameSafe(EC_TAG_BOOTSTRAP_IP)->GetInt(),
1787 request->GetTagByNameSafe(EC_TAG_BOOTSTRAP_PORT)->GetInt());
1788 theApp->ShowConnectionState();
1789 response = new CECPacket(EC_OP_NOOP);
1790 } else {
1791 response = new CECPacket(EC_OP_FAILED);
1792 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Kad is disabled in preferences.")));
1794 break;
1797 // Networks
1798 // These requests are currently used only in the text client
1800 case EC_OP_CONNECT:
1801 if (thePrefs::GetNetworkED2K()) {
1802 response = new CECPacket(EC_OP_STRINGS);
1803 if (theApp->IsConnectedED2K()) {
1804 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Already connected to eD2k.")));
1805 } else {
1806 theApp->serverconnect->ConnectToAnyServer();
1807 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Connecting to eD2k...")));
1810 if (thePrefs::GetNetworkKademlia()) {
1811 if (!response) {
1812 response = new CECPacket(EC_OP_STRINGS);
1814 if (theApp->IsConnectedKad()) {
1815 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Already connected to Kad.")));
1816 } else {
1817 theApp->StartKad();
1818 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Connecting to Kad...")));
1821 if (response) {
1822 theApp->ShowConnectionState();
1823 } else {
1824 response = new CECPacket(EC_OP_FAILED);
1825 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("All networks are disabled.")));
1827 break;
1828 case EC_OP_DISCONNECT:
1829 if (theApp->IsConnected()) {
1830 response = new CECPacket(EC_OP_STRINGS);
1831 if (theApp->IsConnectedED2K()) {
1832 theApp->serverconnect->Disconnect();
1833 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Disconnected from eD2k.")));
1835 if (theApp->IsConnectedKad()) {
1836 theApp->StopKad();
1837 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Disconnected from Kad.")));
1839 theApp->ShowConnectionState();
1840 } else {
1841 response = new CECPacket(EC_OP_NOOP);
1843 break;
1845 if (!response) {
1846 AddLogLineN(CFormat(_("External Connection: invalid opcode received: %#x")) % request->GetOpCode());
1847 wxFAIL;
1848 response = new CECPacket(EC_OP_FAILED);
1849 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Invalid opcode (wrong protocol version?)")));
1851 return response;
1855 * Here notification-based EC. Notification will be sorted by priority for possible throttling.
1859 * Core general status
1861 ECStatusMsgSource::ECStatusMsgSource()
1863 m_last_ed2k_status_sent = 0xffffffff;
1864 m_last_kad_status_sent = 0xffffffff;
1865 m_server = (void *)0xffffffff;
1868 uint32 ECStatusMsgSource::GetEd2kStatus()
1870 if ( theApp->IsConnectedED2K() ) {
1871 return theApp->GetED2KID();
1872 } else if ( theApp->serverconnect->IsConnecting() ) {
1873 return 1;
1874 } else {
1875 return 0;
1879 uint32 ECStatusMsgSource::GetKadStatus()
1881 if ( theApp->IsConnectedKad() ) {
1882 return 1;
1883 } else if ( Kademlia::CKademlia::IsFirewalled() ) {
1884 return 2;
1885 } else if ( Kademlia::CKademlia::IsRunning() ) {
1886 return 3;
1888 return 0;
1891 CECPacket *ECStatusMsgSource::GetNextPacket()
1893 if ( (m_last_ed2k_status_sent != GetEd2kStatus()) ||
1894 (m_last_kad_status_sent != GetKadStatus()) ||
1895 (m_server != theApp->serverconnect->GetCurrentServer()) ) {
1897 m_last_ed2k_status_sent = GetEd2kStatus();
1898 m_last_kad_status_sent = GetKadStatus();
1899 m_server = theApp->serverconnect->GetCurrentServer();
1901 CECPacket *response = new CECPacket(EC_OP_STATS);
1902 response->AddTag(CEC_ConnState_Tag(EC_DETAIL_UPDATE));
1903 return response;
1905 return 0;
1909 * Downloading files
1911 ECPartFileMsgSource::ECPartFileMsgSource()
1913 for (unsigned int i = 0; i < theApp->downloadqueue->GetFileCount(); i++) {
1914 CPartFile *cur_file = theApp->downloadqueue->GetFileByIndex(i);
1915 PARTFILE_STATUS status = { true, false, false, false, true, cur_file };
1916 m_dirty_status[cur_file->GetFileHash()] = status;
1920 void ECPartFileMsgSource::SetDirty(const CPartFile *file)
1922 CMD4Hash filehash = file->GetFileHash();
1923 if ( m_dirty_status.find(filehash) != m_dirty_status.end() ) {
1924 m_dirty_status[filehash].m_dirty = true;;
1928 void ECPartFileMsgSource::SetNew(const CPartFile *file)
1930 CMD4Hash filehash = file->GetFileHash();
1931 wxASSERT ( m_dirty_status.find(filehash) == m_dirty_status.end() );
1932 PARTFILE_STATUS status = { true, false, false, false, true, file };
1933 m_dirty_status[filehash] = status;
1936 void ECPartFileMsgSource::SetCompleted(const CPartFile *file)
1938 CMD4Hash filehash = file->GetFileHash();
1939 wxASSERT ( m_dirty_status.find(filehash) != m_dirty_status.end() );
1941 m_dirty_status[filehash].m_finished = true;
1944 void ECPartFileMsgSource::SetRemoved(const CPartFile *file)
1946 CMD4Hash filehash = file->GetFileHash();
1947 wxASSERT ( m_dirty_status.find(filehash) != m_dirty_status.end() );
1949 m_dirty_status[filehash].m_removed = true;
1952 CECPacket *ECPartFileMsgSource::GetNextPacket()
1954 for(std::map<CMD4Hash, PARTFILE_STATUS>::iterator it = m_dirty_status.begin();
1955 it != m_dirty_status.end(); it++) {
1956 if ( it->second.m_new || it->second.m_dirty || it->second.m_removed) {
1957 CMD4Hash filehash = it->first;
1959 const CPartFile *partfile = it->second.m_file;
1961 CECPacket *packet = new CECPacket(EC_OP_DLOAD_QUEUE);
1962 if ( it->second.m_removed ) {
1963 CECTag tag(EC_TAG_PARTFILE, filehash);
1964 packet->AddTag(tag);
1965 m_dirty_status.erase(it);
1966 } else {
1967 CEC_PartFile_Tag tag(partfile, it->second.m_new ? EC_DETAIL_FULL : EC_DETAIL_UPDATE);
1968 packet->AddTag(tag);
1970 m_dirty_status[filehash].m_new = false;
1971 m_dirty_status[filehash].m_dirty = false;
1973 return packet;
1976 return 0;
1980 * Shared files - similar to downloading
1982 ECKnownFileMsgSource::ECKnownFileMsgSource()
1984 for (unsigned int i = 0; i < theApp->sharedfiles->GetFileCount(); i++) {
1985 const CKnownFile *cur_file = theApp->sharedfiles->GetFileByIndex(i);
1986 KNOWNFILE_STATUS status = { true, false, false, true, cur_file };
1987 m_dirty_status[cur_file->GetFileHash()] = status;
1991 void ECKnownFileMsgSource::SetDirty(const CKnownFile *file)
1993 CMD4Hash filehash = file->GetFileHash();
1994 if ( m_dirty_status.find(filehash) != m_dirty_status.end() ) {
1995 m_dirty_status[filehash].m_dirty = true;;
1999 void ECKnownFileMsgSource::SetNew(const CKnownFile *file)
2001 CMD4Hash filehash = file->GetFileHash();
2002 wxASSERT ( m_dirty_status.find(filehash) == m_dirty_status.end() );
2003 KNOWNFILE_STATUS status = { true, false, false, true, file };
2004 m_dirty_status[filehash] = status;
2007 void ECKnownFileMsgSource::SetRemoved(const CKnownFile *file)
2009 CMD4Hash filehash = file->GetFileHash();
2010 wxASSERT ( m_dirty_status.find(filehash) != m_dirty_status.end() );
2012 m_dirty_status[filehash].m_removed = true;
2015 CECPacket *ECKnownFileMsgSource::GetNextPacket()
2017 for(std::map<CMD4Hash, KNOWNFILE_STATUS>::iterator it = m_dirty_status.begin();
2018 it != m_dirty_status.end(); it++) {
2019 if ( it->second.m_new || it->second.m_dirty || it->second.m_removed) {
2020 CMD4Hash filehash = it->first;
2022 const CKnownFile *partfile = it->second.m_file;
2024 CECPacket *packet = new CECPacket(EC_OP_SHARED_FILES);
2025 if ( it->second.m_removed ) {
2026 CECTag tag(EC_TAG_PARTFILE, filehash);
2027 packet->AddTag(tag);
2028 m_dirty_status.erase(it);
2029 } else {
2030 CEC_SharedFile_Tag tag(partfile, it->second.m_new ? EC_DETAIL_FULL : EC_DETAIL_UPDATE);
2031 packet->AddTag(tag);
2033 m_dirty_status[filehash].m_new = false;
2034 m_dirty_status[filehash].m_dirty = false;
2036 return packet;
2039 return 0;
2043 * Notification about search status
2045 ECSearchMsgSource::ECSearchMsgSource()
2049 CECPacket *ECSearchMsgSource::GetNextPacket()
2051 if ( m_dirty_status.empty() ) {
2052 return 0;
2055 CECPacket *response = new CECPacket(EC_OP_SEARCH_RESULTS);
2056 for(std::map<CMD4Hash, SEARCHFILE_STATUS>::iterator it = m_dirty_status.begin();
2057 it != m_dirty_status.end(); it++) {
2059 if ( it->second.m_new ) {
2060 response->AddTag(CEC_SearchFile_Tag(it->second.m_file, EC_DETAIL_FULL));
2061 it->second.m_new = false;
2062 } else if ( it->second.m_dirty ) {
2063 response->AddTag(CEC_SearchFile_Tag(it->second.m_file, EC_DETAIL_UPDATE));
2068 return response;
2071 void ECSearchMsgSource::FlushStatus()
2073 m_dirty_status.clear();
2076 void ECSearchMsgSource::SetDirty(const CSearchFile *file)
2078 if ( m_dirty_status.count(file->GetFileHash()) ) {
2079 m_dirty_status[file->GetFileHash()].m_dirty = true;
2080 } else {
2081 m_dirty_status[file->GetFileHash()].m_new = true;
2082 m_dirty_status[file->GetFileHash()].m_dirty = true;
2083 m_dirty_status[file->GetFileHash()].m_child_dirty = true;
2084 m_dirty_status[file->GetFileHash()].m_file = file;
2088 void ECSearchMsgSource::SetChildDirty(const CSearchFile *file)
2090 m_dirty_status[file->GetFileHash()].m_child_dirty = true;
2094 * Notification about uploading clients
2096 CECPacket *ECClientMsgSource::GetNextPacket()
2098 return 0;
2102 // Notification iface per-client
2104 ECNotifier::ECNotifier()
2108 ECNotifier::~ECNotifier()
2110 while (m_msg_source.begin() != m_msg_source.end())
2111 Remove_EC_Client(m_msg_source.begin()->first);
2114 CECPacket *ECNotifier::GetNextPacket(ECUpdateMsgSource *msg_source_array[])
2116 CECPacket *packet = 0;
2118 // priority 0 is highest
2120 for(int i = 0; i < EC_STATUS_LAST_PRIO; i++) {
2121 if ( (packet = msg_source_array[i]->GetNextPacket()) != 0 ) {
2122 break;
2125 return packet;
2128 CECPacket *ECNotifier::GetNextPacket(CECServerSocket *sock)
2131 // OnOutput is called for a first time before
2132 // socket is registered
2134 if ( m_msg_source.count(sock) ) {
2135 ECUpdateMsgSource **notifier_array = m_msg_source[sock];
2136 if ( !notifier_array ) {
2137 return 0;
2139 CECPacket *packet = GetNextPacket(notifier_array);
2140 //printf("[EC] next update packet; opcode=%x\n",packet ? packet->GetOpCode() : 0xff);
2141 return packet;
2142 } else {
2143 return 0;
2148 // Interface to notification macros
2150 void ECNotifier::DownloadFile_SetDirty(const CPartFile *file)
2152 for(std::map<CECServerSocket *, ECUpdateMsgSource **>::iterator i = m_msg_source.begin();
2153 i != m_msg_source.end(); ++i) {
2154 CECServerSocket *sock = i->first;
2155 if ( sock->HaveNotificationSupport() ) {
2156 ECUpdateMsgSource **notifier_array = i->second;
2157 static_cast<ECPartFileMsgSource *>(notifier_array[EC_PARTFILE])->SetDirty(file);
2160 NextPacketToSocket();
2163 void ECNotifier::DownloadFile_RemoveFile(const CPartFile *file)
2165 for(std::map<CECServerSocket *, ECUpdateMsgSource **>::iterator i = m_msg_source.begin();
2166 i != m_msg_source.end(); ++i) {
2167 ECUpdateMsgSource **notifier_array = i->second;
2168 static_cast<ECPartFileMsgSource *>(notifier_array[EC_PARTFILE])->SetRemoved(file);
2170 NextPacketToSocket();
2173 void ECNotifier::DownloadFile_RemoveSource(const CPartFile *)
2175 // per-partfile source list is not supported (yet), and IMHO quite useless
2178 void ECNotifier::DownloadFile_AddFile(const CPartFile *file)
2180 for(std::map<CECServerSocket *, ECUpdateMsgSource **>::iterator i = m_msg_source.begin();
2181 i != m_msg_source.end(); ++i) {
2182 ECUpdateMsgSource **notifier_array = i->second;
2183 static_cast<ECPartFileMsgSource *>(notifier_array[EC_PARTFILE])->SetNew(file);
2185 NextPacketToSocket();
2188 void ECNotifier::DownloadFile_AddSource(const CPartFile *)
2190 // per-partfile source list is not supported (yet), and IMHO quite useless
2193 void ECNotifier::SharedFile_AddFile(const CKnownFile *file)
2195 for(std::map<CECServerSocket *, ECUpdateMsgSource **>::iterator i = m_msg_source.begin();
2196 i != m_msg_source.end(); ++i) {
2197 ECUpdateMsgSource **notifier_array = i->second;
2198 static_cast<ECKnownFileMsgSource *>(notifier_array[EC_KNOWN])->SetNew(file);
2200 NextPacketToSocket();
2203 void ECNotifier::SharedFile_RemoveFile(const CKnownFile *file)
2205 for(std::map<CECServerSocket *, ECUpdateMsgSource **>::iterator i = m_msg_source.begin();
2206 i != m_msg_source.end(); ++i) {
2207 ECUpdateMsgSource **notifier_array = i->second;
2208 static_cast<ECKnownFileMsgSource *>(notifier_array[EC_KNOWN])->SetRemoved(file);
2210 NextPacketToSocket();
2213 void ECNotifier::SharedFile_RemoveAllFiles()
2215 // need to figure out what to do here
2218 void ECNotifier::Add_EC_Client(CECServerSocket *sock)
2220 ECUpdateMsgSource **notifier_array = new ECUpdateMsgSource *[EC_STATUS_LAST_PRIO];
2221 notifier_array[EC_STATUS] = new ECStatusMsgSource();
2222 notifier_array[EC_SEARCH] = new ECSearchMsgSource();
2223 notifier_array[EC_PARTFILE] = new ECPartFileMsgSource();
2224 notifier_array[EC_CLIENT] = new ECClientMsgSource();
2225 notifier_array[EC_KNOWN] = new ECKnownFileMsgSource();
2227 m_msg_source[sock] = notifier_array;
2230 void ECNotifier::Remove_EC_Client(CECServerSocket *sock)
2232 if (m_msg_source.count(sock)) {
2233 ECUpdateMsgSource **notifier_array = m_msg_source[sock];
2235 m_msg_source.erase(sock);
2237 for(int i = 0; i < EC_STATUS_LAST_PRIO; i++) {
2238 delete notifier_array[i];
2240 delete [] notifier_array;
2244 void ECNotifier::NextPacketToSocket()
2246 for(std::map<CECServerSocket *, ECUpdateMsgSource **>::iterator i = m_msg_source.begin();
2247 i != m_msg_source.end(); ++i) {
2248 CECServerSocket *sock = i->first;
2249 if ( sock->HaveNotificationSupport() && !sock->DataPending() ) {
2250 ECUpdateMsgSource **notifier_array = i->second;
2251 CECPacket *packet = GetNextPacket(notifier_array);
2252 if ( packet ) {
2253 //printf("[EC] sending update packet; opcode=%x\n",packet->GetOpCode());
2254 sock->SendPacket(packet);
2260 // File_checked_for_headers