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