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.
11 //
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.
16 //
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.
21 //
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
25 //
26
27 #include "config.h" // Needed for VERSION
28
29 #include <ec/cpp/ECMuleSocket.h> // Needed for CECSocket
30
31 #include <common/Format.h> // Needed for CFormat
32
33 #include <common/ClientVersion.h>
34 #include <common/MD5Sum.h>
35
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"
57
58
59 //-------------------- File_Encoder --------------------
60
61
62 /*
63 * Encode 'obtained parts' info to be sent to remote gui
64 */
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:
CKnownFile_Encoder(const CKnownFile * file=0)71 CKnownFile_Encoder(const CKnownFile *file = 0) { m_file = file; }
72
~CKnownFile_Encoder()73 virtual ~CKnownFile_Encoder() {}
74
75 virtual void Encode(CECTag *parent_tag);
76
ResetEncoder()77 virtual void ResetEncoder()
78 {
79 m_enc_data.ResetEncoder();
80 }
81
SetShared()82 virtual void SetShared() { }
IsShared()83 virtual bool IsShared() { return true; }
IsPartFile_Encoder()84 virtual bool IsPartFile_Encoder() { return false; }
GetFile()85 const CKnownFile * GetFile() { return m_file; }
86 };
87
88 /*!
89 * PartStatus strings and gap lists are quite long - RLE encoding will help.
90 *
91 * Instead of sending each time full part-status string, send
92 * RLE encoded difference from previous one.
93 *
94 * PartFileEncoderData class is used for decode only,
95 * while CPartFile_Encoder is used for encode only.
96 */
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;
108
109 // cast inherited member to CPartFile
m_PartFile()110 const CPartFile * m_PartFile() { wxCHECK(m_file->IsCPartFile(), NULL); return static_cast<const CPartFile *>(m_file); }
111 public:
112 // encoder side
CPartFile_Encoder(const CPartFile * file=0)113 CPartFile_Encoder(const CPartFile *file = 0) : CKnownFile_Encoder(file)
114 {
115 m_sourcenameID = 0;
116 m_shared = false;
117 }
118
~CPartFile_Encoder()119 virtual ~CPartFile_Encoder() {}
120
121 // encode - take data from m_file
122 virtual void Encode(CECTag *parent_tag);
123
124 // Encoder may reset history if full info requested
125 virtual void ResetEncoder();
126
SetShared()127 virtual void SetShared() { m_shared = true; }
IsShared()128 virtual bool IsShared() { return m_shared; }
IsPartFile_Encoder()129 virtual bool IsPartFile_Encoder() { return true; }
130 };
131
132 class CFileEncoderMap : public std::map<uint32, CKnownFile_Encoder*> {
133 typedef std::set<uint32> IDSet;
134 public:
135 ~CFileEncoderMap();
136 void UpdateEncoders();
137 };
138
~CFileEncoderMap()139 CFileEncoderMap::~CFileEncoderMap()
140 {
141 // DeleteContents() causes infinite recursion here!
142 for (iterator it = begin(); it != end(); ++it) {
143 delete it->second;
144 }
145 }
146
147 // Check if encoder contains files that are no longer used
148 // or if we have new files without encoder yet.
UpdateEncoders()149 void CFileEncoderMap::UpdateEncoders()
150 {
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]);
160 }
161 }
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;
172 }
173 curr_files.insert(id);
174 if (!count(id)) {
175 (*this)[id] = new CKnownFile_Encoder(shares[i]);
176 }
177 }
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);
185 }
186 }
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);
192 }
193 }
194
195
196 //-------------------- CECServerSocket --------------------
197
198 class CECServerSocket : public CECMuleSocket
199 {
200 public:
201 CECServerSocket(ECNotifier *notifier);
202 virtual ~CECServerSocket();
203
204 virtual const CECPacket *OnPacketReceived(const CECPacket *packet, uint32 trueSize);
205 virtual void OnLost();
206
207 virtual void WriteDoneAndQueueEmpty();
208
ResetLog()209 void ResetLog() { m_LoggerAccess.Reset(); }
210 private:
211 ECNotifier *m_ec_notifier;
212
213 const CECPacket *Authenticate(const CECPacket *);
214
215 enum {
216 CONN_INIT,
217 CONN_SALT_SENT,
218 CONN_ESTABLISHED,
219 CONN_FAILED
220 } m_conn_state;
221
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);
227
IsAuthorized()228 virtual bool IsAuthorized() { return m_conn_state == CONN_ESTABLISHED; }
229 };
230
231
CECServerSocket(ECNotifier * notifier)232 CECServerSocket::CECServerSocket(ECNotifier *notifier)
233 :
234 CECMuleSocket(true),
235 m_conn_state(CONN_INIT),
236 m_passwd_salt(GetRandomUint64())
237 {
238 wxASSERT(theApp->ECServerHandler);
239 theApp->ECServerHandler->AddSocket(this);
240 m_ec_notifier = notifier;
241 }
242
243
~CECServerSocket()244 CECServerSocket::~CECServerSocket()
245 {
246 wxASSERT(theApp->ECServerHandler);
247 theApp->ECServerHandler->RemoveSocket(this);
248 }
249
250
OnPacketReceived(const CECPacket * packet,uint32 trueSize)251 const CECPacket *CECServerSocket::OnPacketReceived(const CECPacket *packet, uint32 trueSize)
252 {
253 packet->DebugPrint(true, trueSize);
254
255 const CECPacket *reply = NULL;
256
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();
261 }
262
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);
270 }
271 return reply;
272 }
273
274
OnLost()275 void CECServerSocket::OnLost()
276 {
277 AddLogLineN(_("External connection closed."));
278 theApp->ECServerHandler->m_ec_notifier->Remove_EC_Client(this);
279 DestroySocket();
280 }
281
WriteDoneAndQueueEmpty()282 void CECServerSocket::WriteDoneAndQueueEmpty()
283 {
284 if ( HaveNotificationSupport() && (m_conn_state == CONN_ESTABLISHED) ) {
285 CECPacket *packet = m_ec_notifier->GetNextPacket(this);
286 if ( packet ) {
287 SendPacket(packet);
288 }
289 } else {
290 //printf("[EC] %p: WriteDoneAndQueueEmpty but notification disabled\n", this);
291 }
292 }
293
294 //-------------------- ExternalConn --------------------
295
296 #ifndef ASIO_SOCKETS
297 enum
298 { // id for sockets
299 SERVER_ID = 1000
300 };
301
302
BEGIN_EVENT_TABLE(ExternalConn,wxEvtHandler)303 BEGIN_EVENT_TABLE(ExternalConn, wxEvtHandler)
304 EVT_SOCKET(SERVER_ID, ExternalConn::OnServerEvent)
305 END_EVENT_TABLE()
306 #endif
307
308
309 ExternalConn::ExternalConn(amuleIPV4Address addr, wxString *msg)
310 {
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;
320 }
321
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);
329
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);
340 }
341 } else {
342 *msg += wxT("External connections disabled in config file\n");
343 AddLogLineN(_("External connections disabled in config file"));
344 }
345 m_ec_notifier = new ECNotifier();
346 }
347
348
~ExternalConn()349 ExternalConn::~ExternalConn()
350 {
351 KillAllSockets();
352 delete m_ECServer;
353 delete m_ec_notifier;
354 }
355
356
AddSocket(CECServerSocket * s)357 void ExternalConn::AddSocket(CECServerSocket *s)
358 {
359 wxASSERT(s);
360 socket_list.insert(s);
361 }
362
363
RemoveSocket(CECServerSocket * s)364 void ExternalConn::RemoveSocket(CECServerSocket *s)
365 {
366 wxASSERT(s);
367 socket_list.erase(s);
368 }
369
370
KillAllSockets()371 void ExternalConn::KillAllSockets()
372 {
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();
381 }
382 socket_list.clear();
383 }
384
385
ResetAllLogs()386 void ExternalConn::ResetAllLogs()
387 {
388 SocketSet::iterator it = socket_list.begin();
389 while (it != socket_list.end()) {
390 CECServerSocket *s = *(it++);
391 s->ResetLog();
392 }
393 }
394
395
396 #ifndef ASIO_SOCKETS
OnServerEvent(wxSocketEvent & WXUNUSED (event))397 void ExternalConn::OnServerEvent(wxSocketEvent& WXUNUSED(event))
398 {
399 m_ECServer->OnAccept();
400 }
401 #endif
402
403
OnAccept()404 void CExternalConnListener::OnAccept()
405 {
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"));
416 }
417
418 }
419
420 //
421 // Authentication
422 //
Authenticate(const CECPacket * request)423 const CECPacket *CECServerSocket::Authenticate(const CECPacket *request)
424 {
425 CECPacket *response;
426
427 if (request == NULL) {
428 return new CECPacket(EC_OP_AUTH_FAIL);
429 }
430
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!"));
434
435 return new CECPacket(EC_OP_AUTH_FAIL);
436 }
437
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);
443
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;
469 //
470 // So far ok, check capabilities of client
471 //
472 if (request->GetTagByName(EC_TAG_CAN_ZLIB)) {
473 m_my_flags |= EC_FLAG_ZLIB;
474 }
475 if (request->GetTagByName(EC_TAG_CAN_UTF8_NUMBERS)) {
476 m_my_flags |= EC_FLAG_UTF8_NUMBERS;
477 }
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));
487 }
488 } else {
489 response = new CECPacket(EC_OP_AUTH_FAIL);
490 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Missing protocol version tag.")));
491 }
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;
495
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;
505
506 passh.Decode(MD5Sum(thePrefs::ECPassword().Lower() + saltHash).GetHash());
507
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.");
517 }
518
519 response = new CECPacket(EC_OP_AUTH_FAIL);
520 response->AddTag(CECTag(EC_TAG_STRING, err));
521 AddLogLineN(wxGetTranslation(err));
522 }
523 }
524 } else {
525 response = new CECPacket(EC_OP_AUTH_FAIL);
526 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Invalid request, please authenticate first.")));
527 }
528
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);
535 }
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()));
540 }
541 // Access denied!
542 AddLogLineN(CFormat(_("Unauthorized access attempt from %s. Connection closed.")) % GetPeer() );
543 m_conn_state = CONN_FAILED;
544 }
545
546 return response;
547 }
548
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)
551 {
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++;
561 }
562 response->AddTag(tag);
563 //printf("send Log tag %d %d\n", FirstEntry, entries);
564 }
565 }
566
567 static CECPacket *Get_EC_Response_StatRequest(const CECPacket *request, CLoggerAccess &LoggerAccess)
568 {
569 CECPacket *response = new CECPacket(EC_OP_STATS);
570
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
595 {
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()));
603 }
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();
620 }
621 response->AddTag(CECTag(EC_TAG_STATS_BUDDY_IP, BuddyIP));
622 response->AddTag(CECTag(EC_TAG_STATS_BUDDY_PORT, BuddyPort));
623 }
624 case EC_DETAIL_UPDATE:
625 break;
626 };
627
628 return response;
629 }
630
631 static CECPacket *Get_EC_Response_GetSharedFiles(const CECPacket *request, CFileEncoderMap &encoders)
632 {
633 wxASSERT(request->GetOpCode() == EC_OP_GET_SHARED_FILES);
634
635 CECPacket *response = new CECPacket(EC_OP_SHARED_FILES);
636
637 EC_DETAIL_LEVEL detail_level = request->GetDetailLevel();
638 //
639 // request can contain list of queried items
640 CTagSet<uint32, EC_TAG_KNOWNFILE> queryitems(request);
641
642 encoders.UpdateEncoders();
643
644 for (uint32 i = 0; i < theApp->sharedfiles->GetFileCount(); ++i) {
645 const CKnownFile *cur_file = theApp->sharedfiles->GetFileByIndex(i);
646
647 if ( !cur_file || (!queryitems.empty() && !queryitems.count(cur_file->ECID())) ) {
648 continue;
649 }
650
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();
655 }
656 enc->Encode(&filetag);
657 response->AddTag(filetag);
658 }
659 return response;
660 }
661
662 static CECPacket *Get_EC_Response_GetUpdate(CFileEncoderMap &encoders, CObjTagMap &tagmap)
663 {
664 CECPacket *response = new CECPacket(EC_OP_SHARED_FILES);
665
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);
677
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);
686 }
687 }
688
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;
699 }
700 CValueMap &valuemap = tagmap.GetValueMap(cur_client->ECID());
701 clients.AddTag(CEC_UpDownClient_Tag(cur_client, EC_DETAIL_INC_UPDATE, &valuemap));
702 }
703 response->AddTag(clients);
704
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));
713 }
714 response->AddTag(servers);
715
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));
722 }
723 response->AddTag(friends);
724
725 return response;
726 }
727
728 static CECPacket *Get_EC_Response_GetClientQueue(const CECPacket *request, CObjTagMap &tagmap, int op)
729 {
730 CECPacket *response = new CECPacket(op);
731
732 EC_DETAIL_LEVEL detail_level = request->GetDetailLevel();
733
734 //
735 // request can contain list of queried items
736 // (not for incremental update of course)
737 CTagSet<uint32, EC_TAG_CLIENT> queryitems(request);
738
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();
743
744 if (!cur_client) { // shouldn't happen
745 continue;
746 }
747 if (!queryitems.empty() && !queryitems.count(cur_client->ECID())) {
748 continue;
749 }
750 CValueMap *valuemap = NULL;
751 if (detail_level == EC_DETAIL_INC_UPDATE) {
752 valuemap = &tagmap.GetValueMap(cur_client->ECID());
753 }
754 CEC_UpDownClient_Tag cli_tag(cur_client, detail_level, valuemap);
755
756 response->AddTag(cli_tag);
757 }
758
759 return response;
760 }
761
762
763 static CECPacket *Get_EC_Response_GetDownloadQueue(const CECPacket *request, CFileEncoderMap &encoders)
764 {
765 CECPacket *response = new CECPacket(EC_OP_DLOAD_QUEUE);
766
767 EC_DETAIL_LEVEL detail_level = request->GetDetailLevel();
768 //
769 // request can contain list of queried items
770 CTagSet<uint32, EC_TAG_PARTFILE> queryitems(request);
771
772 encoders.UpdateEncoders();
773
774 for (unsigned int i = 0; i < theApp->downloadqueue->GetFileCount(); i++) {
775 CPartFile *cur_file = theApp->downloadqueue->GetFileByIndex(i);
776
777 if ( !queryitems.empty() && !queryitems.count(cur_file->ECID()) ) {
778 continue;
779 }
780
781 CEC_PartFile_Tag filetag(cur_file, detail_level);
782
783 CPartFile_Encoder * enc = static_cast<CPartFile_Encoder *>(encoders[cur_file->ECID()]);
784 if ( detail_level != EC_DETAIL_UPDATE ) {
785 enc->ResetEncoder();
786 }
787 enc->Encode(&filetag);
788
789 response->AddTag(filetag);
790 }
791 return response;
792 }
793
794
795 static CECPacket *Get_EC_Response_PartFile_Cmd(const CECPacket *request)
796 {
797 CECPacket *response = NULL;
798
799 // request can contain multiple files.
800 for (CECPacket::const_iterator it1 = request->begin(); it1 != request->end(); ++it1) {
801 const CECTag &hashtag = *it1;
802
803 wxASSERT(hashtag.GetTagName() == EC_TAG_PARTFILE);
804
805 CMD4Hash hash = hashtag.GetMD4Data();
806 CPartFile *pfile = theApp->downloadqueue->GetFileByID( hash );
807
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;
814 }
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);
842 }
843 }
844 break;
845 case EC_OP_PARTFILE_DELETE:
846 if ( thePrefs::StartNextFile() && (pfile->GetStatus() != PS_PAUSED) ) {
847 theApp->downloadqueue->StartNextFile(pfile);
848 }
849 pfile->Delete();
850 break;
851
852 case EC_OP_PARTFILE_SET_CAT:
853 pfile->SetCategory(hashtag.GetFirstTagSafe()->GetInt());
854 break;
855
856 default:
857 response = new CECPacket(EC_OP_FAILED);
858 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("OOPS! OpCode processing error!")));
859 break;
860 }
861 }
862 if (!response) {
863 response = new CECPacket(EC_OP_NOOP);
864 }
865 return response;
866 }
867
868 static CECPacket *Get_EC_Response_Server_Add(const CECPacket *request)
869 {
870 CECPacket *response = NULL;
871
872 wxString full_addr = request->GetTagByNameSafe(EC_TAG_SERVER_ADDRESS)->GetStringData();
873 wxString name = request->GetTagByNameSafe(EC_TAG_SERVER_NAME)->GetStringData();
874
875 wxString s_ip = full_addr.Left(full_addr.Find(':'));
876 wxString s_port = full_addr.Mid(full_addr.Find(':') + 1);
877
878 long port = StrToULong(s_port);
879 CServer* toadd = new CServer(port, s_ip);
880 toadd->SetListName(name.IsEmpty() ? full_addr : name);
881
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;
888 }
889
890 return response;
891 }
892
893 static CECPacket *Get_EC_Response_Server(const CECPacket *request)
894 {
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;
906 }
907 }
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")));
921 }
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);
931 }
932 } else {
933 response = new CECPacket(EC_OP_FAILED);
934 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("eD2k is disabled in preferences.")));
935 }
936 break;
937 }
938 if (!response) {
939 response = new CECPacket(EC_OP_FAILED);
940 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("OOPS! OpCode processing error!")));
941 }
942 return response;
943 }
944
945
946 static CECPacket *Get_EC_Response_Friend(const CECPacket *request)
947 {
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);
957 }
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);
966 }
967 }
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);
975 }
976 }
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);
984 }
985 }
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.
991
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);
998 }
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);
1004 }
1005 }
1006 #endif
1007 }
1008
1009 if (!response) {
1010 response = new CECPacket(EC_OP_FAILED);
1011 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("OOPS! OpCode processing error!")));
1012 }
1013 return response;
1014 }
1015
1016
1017 static CECPacket *Get_EC_Response_Search_Results(const CECPacket *request)
1018 {
1019 CECPacket *response = new CECPacket(EC_OP_SEARCH_RESULTS);
1020
1021 EC_DETAIL_LEVEL detail_level = request->GetDetailLevel();
1022 //
1023 // request can contain list of queried items
1024 CTagSet<uint32, EC_TAG_SEARCHFILE> queryitems(request);
1025
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;
1032 }
1033 response->AddTag(CEC_SearchFile_Tag(sf, detail_level));
1034 }
1035 return response;
1036 }
1037
1038 static CECPacket *Get_EC_Response_Search_Results(CObjTagMap &tagmap)
1039 {
1040 CECPacket *response = new CECPacket(EC_OP_SEARCH_RESULTS);
1041
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));
1055 }
1056 }
1057 }
1058 return response;
1059 }
1060
1061 static CECPacket *Get_EC_Response_Search_Results_Download(const CECPacket *request)
1062 {
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);
1069 }
1070 return response;
1071 }
1072
1073 static CECPacket *Get_EC_Response_Search_Stop(const CECPacket *WXUNUSED(request))
1074 {
1075 CECPacket *reply = new CECPacket(EC_OP_MISC_DATA);
1076 theApp->searchlist->StopSearch();
1077 return reply;
1078 }
1079
1080 static CECPacket *Get_EC_Response_Search(const CECPacket *request)
1081 {
1082 wxString response;
1083
1084 const CEC_Search_Tag *search_request = static_cast<const CEC_Search_Tag *>(request->GetFirstTagSafe());
1085 theApp->searchlist->RemoveResults(0xffffffff);
1086
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();
1094
1095
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;
1106 }
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;
1116 }
1117 break;
1118 }
1119 case EC_SEARCH_WEB:
1120 response = wxTRANSLATE("WebSearch from remote interface makes no sense.");
1121 break;
1122 }
1123
1124 CECPacket *reply = new CECPacket(op);
1125 // error or search in progress
1126 reply->AddTag(CECTag(EC_TAG_STRING, response));
1127
1128 return reply;
1129 }
1130
1131 static CECPacket *Get_EC_Response_Set_SharedFile_Prio(const CECPacket *request)
1132 {
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;
1141 }
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);
1148 }
1149 Notify_SharedFilesUpdateItem(cur_file);
1150 }
1151
1152 return response;
1153 }
1154
1155 void CPartFile_Encoder::Encode(CECTag *parent)
1156 {
1157 //
1158 // Source part frequencies
1159 //
1160 CKnownFile_Encoder::Encode(parent);
1161
1162 //
1163 // Gaps
1164 //
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);
1169
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());
1174 }
1175
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));
1181 }
1182 delete[] gap_enc_data;
1183
1184 //
1185 // Requested blocks
1186 //
1187 ArrayOfUInts64 req_buffer;
1188 const CPartFile::CReqBlockPtrList& requestedblocks = m_PartFile()->GetRequestedBlockList();
1189 CPartFile::CReqBlockPtrList::const_iterator curr_pos2 = requestedblocks.begin();
1190
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);
1195 }
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));
1200 }
1201 delete[] req_enc_data;
1202
1203 //
1204 // Source names
1205 //
1206 // First count occurrence of all source names
1207 //
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;
1216 }
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++;
1223 }
1224 }
1225 //
1226 // Go through our last list
1227 //
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;
1245 }
1246 // remove it from nameMap so that only new names are left there
1247 nameMap.erase(itm);
1248 }
1249 }
1250 //
1251 // Add new names
1252 //
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);
1261 }
1262 if (sourceNames.HasChildTags()) {
1263 parent->AddTag(sourceNames);
1264 }
1265
1266 }
1267
1268 void CPartFile_Encoder::ResetEncoder()
1269 {
1270 CKnownFile_Encoder::ResetEncoder();
1271 m_gap_status.ResetEncoder();
1272 m_req_status.ResetEncoder();
1273 }
1274
1275 void CKnownFile_Encoder::Encode(CECTag *parent)
1276 {
1277 //
1278 // Source part frequencies
1279 //
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));
1291 }
1292 delete[] part_enc_data;
1293 }
1294 }
1295
1296 static CECPacket *GetStatsGraphs(const CECPacket *request)
1297 {
1298 CECPacket *response = NULL;
1299
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();
1306 }
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.")));
1319 }
1320 break;
1321 }
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;
1329 }
1330 if (!response) {
1331 response = new CECPacket(EC_OP_FAILED);
1332 // Unknown reason
1333 }
1334
1335 return response;
1336 }
1337
1338 CECPacket *CECServerSocket::ProcessRequest2(const CECPacket *request)
1339 {
1340
1341 if ( !request ) {
1342 return 0;
1343 }
1344
1345 CECPacket *response = NULL;
1346
1347 switch (request->GetOpCode()) {
1348 //
1349 // Misc commands
1350 //
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
1356 {
1357 wxCloseEvent evt;
1358 evt.SetCanVeto(false);
1359 theApp->ShutDown(evt);
1360 }
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.")));
1367 }
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();
1377 }
1378 AddLogLineC(CFormat(_("ExternalConn: adding link '%s'.")) % link);
1379 if (response) {
1380 delete response;
1381 }
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.")));
1388 }
1389 }
1390 break;
1391 //
1392 // Status requests
1393 //
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;
1402 //
1403 //
1404 //
1405 case EC_OP_GET_SHARED_FILES:
1406 if ( request->GetDetailLevel() != EC_DETAIL_INC_UPDATE ) {
1407 response = Get_EC_Response_GetSharedFiles(request, m_FileEncoder);
1408 }
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);
1413 }
1414 break;
1415 //
1416 // This will evolve into an update-all for inc tags
1417 //
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);
1421 }
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);
1451 }
1452 if (!file) {
1453 response = new CECPacket(EC_OP_FAILED);
1454 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("File not found.")));
1455 break;
1456 }
1457 if (newName.IsEmpty()) {
1458 response = new CECPacket(EC_OP_FAILED);
1459 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Invalid file name.")));
1460 break;
1461 }
1462
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.")));
1468 }
1469
1470 break;
1471 }
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());
1477 }
1478 }
1479 theApp->downloadqueue->ClearCompleted(toClear);
1480 response = new CECPacket(EC_OP_NOOP);
1481 break;
1482 }
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);
1491 }
1492 response = new CECPacket(EC_OP_NOOP);
1493 break;
1494 }
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);
1502 }
1503 response = new CECPacket(EC_OP_NOOP);
1504 break;
1505 }
1506
1507 //
1508 // Server commands
1509 //
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;
1523 }
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
1530 ) {
1531 response->AddTag(CEC_Server_Tag(*it, detail_level));
1532 }
1533 }
1534 break;
1535 case EC_OP_SERVER_UPDATE_FROM_URL: {
1536 wxString url = request->GetFirstTagSafe()->GetStringData();
1537
1538 // Save the new url, and update the UI (if not amuled).
1539 Notify_ServersURLChanged(url);
1540 thePrefs::SetEd2kServersUrl(url);
1541
1542 theApp->serverlist->UpdateServerMetFromURL(url);
1543 response = new CECPacket(EC_OP_NOOP);
1544 break;
1545 }
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);
1553 }
1554 const CECTag * prioTag = request->GetTagByName(EC_TAG_SERVER_PRIO);
1555 if (prioTag) {
1556 theApp->serverlist->SetServerPrio(server, prioTag->GetInt());
1557 }
1558 }
1559 response = new CECPacket(EC_OP_NOOP);
1560 break;
1561 }
1562 //
1563 // Friends
1564 //
1565 case EC_OP_FRIEND:
1566 response = Get_EC_Response_Friend(request);
1567 break;
1568
1569 //
1570 // IPFilter
1571 //
1572 case EC_OP_IPFILTER_RELOAD:
1573 NotifyAlways_IPFilter_Reload();
1574 response = new CECPacket(EC_OP_NOOP);
1575 break;
1576
1577 case EC_OP_IPFILTER_UPDATE: {
1578 wxString url = request->GetFirstTagSafe()->GetStringData();
1579 if (url.IsEmpty()) {
1580 url = thePrefs::IPFilterURL();
1581 }
1582 NotifyAlways_IPFilter_Update(url);
1583 response = new CECPacket(EC_OP_NOOP);
1584 break;
1585 }
1586 //
1587 // Search
1588 //
1589 case EC_OP_SEARCH_START:
1590 response = Get_EC_Response_Search(request);
1591 break;
1592
1593 case EC_OP_SEARCH_STOP:
1594 response = Get_EC_Response_Search_Stop(request);
1595 break;
1596
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);
1602 }
1603 break;
1604
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;
1610
1611 case EC_OP_DOWNLOAD_SEARCH_RESULT:
1612 response = Get_EC_Response_Search_Results_Download(request);
1613 break;
1614 //
1615 // Preferences
1616 //
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();
1625 }
1626 if (thePrefs::IsFilteringServers()) {
1627 theApp->serverlist->FilterServers();
1628 }
1629 if (!thePrefs::GetNetworkED2K() && theApp->IsConnectedED2K()) {
1630 theApp->DisconnectED2K();
1631 }
1632 if (!thePrefs::GetNetworkKademlia() && theApp->IsConnectedKad()) {
1633 theApp->StopKad();
1634 }
1635 response = new CECPacket(EC_OP_NOOP);
1636 break;
1637
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()));
1647 }
1648 Notify_CategoryAdded();
1649 } else {
1650 response = new CECPacket(EC_OP_NOOP);
1651 }
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()));
1662 }
1663 Notify_CategoryUpdate(tag.GetInt());
1664 } else {
1665 response = new CECPacket(EC_OP_NOOP);
1666 }
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);
1673 }
1674 response = new CECPacket(EC_OP_NOOP);
1675 break;
1676
1677 //
1678 // Logging
1679 //
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());
1686 }
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());
1695 }
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:
1715 {
1716 wxString tmp = theApp->GetLog(false);
1717 if (tmp.Last() == '\n') {
1718 tmp.RemoveLast();
1719 }
1720 response = new CECPacket(EC_OP_LOG);
1721 response->AddTag(CECTag(EC_TAG_STRING, tmp.AfterLast('\n')));
1722 }
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;
1732 //
1733 // Statistics
1734 //
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;
1745 }
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()));
1749 }
1750 break;
1751 }
1752
1753 //
1754 // Kad
1755 //
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();
1762 }
1763 } else {
1764 response = new CECPacket(EC_OP_FAILED);
1765 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Kad is disabled in preferences.")));
1766 }
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();
1775
1776 // Save the new url, and update the UI (if not amuled).
1777 Notify_NodesURLChanged(url);
1778 thePrefs::SetKadNodesUrl(url);
1779
1780 theApp->UpdateNotesDat(url);
1781 response = new CECPacket(EC_OP_NOOP);
1782 break;
1783 }
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.")));
1793 }
1794 break;
1795
1796 //
1797 // Networks
1798 // These requests are currently used only in the text client
1799 //
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...")));
1808 }
1809 }
1810 if (thePrefs::GetNetworkKademlia()) {
1811 if (!response) {
1812 response = new CECPacket(EC_OP_STRINGS);
1813 }
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...")));
1819 }
1820 }
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.")));
1826 }
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.")));
1834 }
1835 if (theApp->IsConnectedKad()) {
1836 theApp->StopKad();
1837 response->AddTag(CECTag(EC_TAG_STRING, wxTRANSLATE("Disconnected from Kad.")));
1838 }
1839 theApp->ShowConnectionState();
1840 } else {
1841 response = new CECPacket(EC_OP_NOOP);
1842 }
1843 break;
1844 }
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?)")));
1850 }
1851 return response;
1852 }
1853
1854 /*
1855 * Here notification-based EC. Notification will be sorted by priority for possible throttling.
1856 */
1857
1858 /*
1859 * Core general status
1860 */
1861 ECStatusMsgSource::ECStatusMsgSource()
1862 {
1863 m_last_ed2k_status_sent = 0xffffffff;
1864 m_last_kad_status_sent = 0xffffffff;
1865 m_server = (void *)0xffffffff;
1866 }
1867
1868 uint32 ECStatusMsgSource::GetEd2kStatus()
1869 {
1870 if ( theApp->IsConnectedED2K() ) {
1871 return theApp->GetED2KID();
1872 } else if ( theApp->serverconnect->IsConnecting() ) {
1873 return 1;
1874 } else {
1875 return 0;
1876 }
1877 }
1878
1879 uint32 ECStatusMsgSource::GetKadStatus()
1880 {
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;
1887 }
1888 return 0;
1889 }
1890
1891 CECPacket *ECStatusMsgSource::GetNextPacket()
1892 {
1893 if ( (m_last_ed2k_status_sent != GetEd2kStatus()) ||
1894 (m_last_kad_status_sent != GetKadStatus()) ||
1895 (m_server != theApp->serverconnect->GetCurrentServer()) ) {
1896
1897 m_last_ed2k_status_sent = GetEd2kStatus();
1898 m_last_kad_status_sent = GetKadStatus();
1899 m_server = theApp->serverconnect->GetCurrentServer();
1900
1901 CECPacket *response = new CECPacket(EC_OP_STATS);
1902 response->AddTag(CEC_ConnState_Tag(EC_DETAIL_UPDATE));
1903 return response;
1904 }
1905 return 0;
1906 }
1907
1908 /*
1909 * Downloading files
1910 */
1911 ECPartFileMsgSource::ECPartFileMsgSource()
1912 {
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;
1917 }
1918 }
1919
1920 void ECPartFileMsgSource::SetDirty(const CPartFile *file)
1921 {
1922 CMD4Hash filehash = file->GetFileHash();
1923 if ( m_dirty_status.find(filehash) != m_dirty_status.end() ) {
1924 m_dirty_status[filehash].m_dirty = true;;
1925 }
1926 }
1927
1928 void ECPartFileMsgSource::SetNew(const CPartFile *file)
1929 {
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;
1934 }
1935
1936 void ECPartFileMsgSource::SetCompleted(const CPartFile *file)
1937 {
1938 CMD4Hash filehash = file->GetFileHash();
1939 wxASSERT ( m_dirty_status.find(filehash) != m_dirty_status.end() );
1940
1941 m_dirty_status[filehash].m_finished = true;
1942 }
1943
1944 void ECPartFileMsgSource::SetRemoved(const CPartFile *file)
1945 {
1946 CMD4Hash filehash = file->GetFileHash();
1947 wxASSERT ( m_dirty_status.find(filehash) != m_dirty_status.end() );
1948
1949 m_dirty_status[filehash].m_removed = true;
1950 }
1951
1952 CECPacket *ECPartFileMsgSource::GetNextPacket()
1953 {
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;
1958
1959 const CPartFile *partfile = it->second.m_file;
1960
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);
1969 }
1970 m_dirty_status[filehash].m_new = false;
1971 m_dirty_status[filehash].m_dirty = false;
1972
1973 return packet;
1974 }
1975 }
1976 return 0;
1977 }
1978
1979 /*
1980 * Shared files - similar to downloading
1981 */
1982 ECKnownFileMsgSource::ECKnownFileMsgSource()
1983 {
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;
1988 }
1989 }
1990
1991 void ECKnownFileMsgSource::SetDirty(const CKnownFile *file)
1992 {
1993 CMD4Hash filehash = file->GetFileHash();
1994 if ( m_dirty_status.find(filehash) != m_dirty_status.end() ) {
1995 m_dirty_status[filehash].m_dirty = true;;
1996 }
1997 }
1998
1999 void ECKnownFileMsgSource::SetNew(const CKnownFile *file)
2000 {
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;
2005 }
2006
2007 void ECKnownFileMsgSource::SetRemoved(const CKnownFile *file)
2008 {
2009 CMD4Hash filehash = file->GetFileHash();
2010 wxASSERT ( m_dirty_status.find(filehash) != m_dirty_status.end() );
2011
2012 m_dirty_status[filehash].m_removed = true;
2013 }
2014
2015 CECPacket *ECKnownFileMsgSource::GetNextPacket()
2016 {
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;
2021
2022 const CKnownFile *partfile = it->second.m_file;
2023
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);
2032 }
2033 m_dirty_status[filehash].m_new = false;
2034 m_dirty_status[filehash].m_dirty = false;
2035
2036 return packet;
2037 }
2038 }
2039 return 0;
2040 }
2041
2042 /*
2043 * Notification about search status
2044 */
2045 ECSearchMsgSource::ECSearchMsgSource()
2046 {
2047 }
2048
2049 CECPacket *ECSearchMsgSource::GetNextPacket()
2050 {
2051 if ( m_dirty_status.empty() ) {
2052 return 0;
2053 }
2054
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++) {
2058
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));
2064 }
2065
2066 }
2067
2068 return response;
2069 }
2070
2071 void ECSearchMsgSource::FlushStatus()
2072 {
2073 m_dirty_status.clear();
2074 }
2075
2076 void ECSearchMsgSource::SetDirty(const CSearchFile *file)
2077 {
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;
2085 }
2086 }
2087
2088 void ECSearchMsgSource::SetChildDirty(const CSearchFile *file)
2089 {
2090 m_dirty_status[file->GetFileHash()].m_child_dirty = true;
2091 }
2092
2093 /*
2094 * Notification about uploading clients
2095 */
2096 CECPacket *ECClientMsgSource::GetNextPacket()
2097 {
2098 return 0;
2099 }
2100
2101 //
2102 // Notification iface per-client
2103 //
2104 ECNotifier::ECNotifier()
2105 {
2106 }
2107
2108 ECNotifier::~ECNotifier()
2109 {
2110 while (m_msg_source.begin() != m_msg_source.end())
2111 Remove_EC_Client(m_msg_source.begin()->first);
2112 }
2113
2114 CECPacket *ECNotifier::GetNextPacket(ECUpdateMsgSource *msg_source_array[])
2115 {
2116 CECPacket *packet = 0;
2117 //
2118 // priority 0 is highest
2119 //
2120 for(int i = 0; i < EC_STATUS_LAST_PRIO; i++) {
2121 if ( (packet = msg_source_array[i]->GetNextPacket()) != 0 ) {
2122 break;
2123 }
2124 }
2125 return packet;
2126 }
2127
2128 CECPacket *ECNotifier::GetNextPacket(CECServerSocket *sock)
2129 {
2130 //
2131 // OnOutput is called for a first time before
2132 // socket is registered
2133 //
2134 if ( m_msg_source.count(sock) ) {
2135 ECUpdateMsgSource **notifier_array = m_msg_source[sock];
2136 if ( !notifier_array ) {
2137 return 0;
2138 }
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;
2144 }
2145 }
2146
2147 //
2148 // Interface to notification macros
2149 //
2150 void ECNotifier::DownloadFile_SetDirty(const CPartFile *file)
2151 {
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);
2158 }
2159 }
2160 NextPacketToSocket();
2161 }
2162
2163 void ECNotifier::DownloadFile_RemoveFile(const CPartFile *file)
2164 {
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);
2169 }
2170 NextPacketToSocket();
2171 }
2172
2173 void ECNotifier::DownloadFile_RemoveSource(const CPartFile *)
2174 {
2175 // per-partfile source list is not supported (yet), and IMHO quite useless
2176 }
2177
2178 void ECNotifier::DownloadFile_AddFile(const CPartFile *file)
2179 {
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);
2184 }
2185 NextPacketToSocket();
2186 }
2187
2188 void ECNotifier::DownloadFile_AddSource(const CPartFile *)
2189 {
2190 // per-partfile source list is not supported (yet), and IMHO quite useless
2191 }
2192
2193 void ECNotifier::SharedFile_AddFile(const CKnownFile *file)
2194 {
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);
2199 }
2200 NextPacketToSocket();
2201 }
2202
2203 void ECNotifier::SharedFile_RemoveFile(const CKnownFile *file)
2204 {
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);
2209 }
2210 NextPacketToSocket();
2211 }
2212
2213 void ECNotifier::SharedFile_RemoveAllFiles()
2214 {
2215 // need to figure out what to do here
2216 }
2217
2218 void ECNotifier::Add_EC_Client(CECServerSocket *sock)
2219 {
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();
2226
2227 m_msg_source[sock] = notifier_array;
2228 }
2229
2230 void ECNotifier::Remove_EC_Client(CECServerSocket *sock)
2231 {
2232 if (m_msg_source.count(sock)) {
2233 ECUpdateMsgSource **notifier_array = m_msg_source[sock];
2234
2235 m_msg_source.erase(sock);
2236
2237 for(int i = 0; i < EC_STATUS_LAST_PRIO; i++) {
2238 delete notifier_array[i];
2239 }
2240 delete [] notifier_array;
2241 }
2242 }
2243
2244 void ECNotifier::NextPacketToSocket()
2245 {
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);
2255 }
2256 }
2257 }
2258 }
2259
2260 // File_checked_for_headers
2261