Upstream tarball 9608
[amule.git] / src / BaseClient.cpp
blob7ba1b01ee93cabbccaf06520deaa622c4422d7de
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include <wx/wx.h>
27 #include <wx/mstream.h>
28 #include <wx/tokenzr.h>
30 #include "updownclient.h" // Needed for CUpDownClient
32 #include <protocol/Protocols.h>
33 #include <protocol/ed2k/Client2Client/TCP.h>
34 #include <protocol/ed2k/ClientSoftware.h>
35 #include <protocol/kad/Client2Client/UDP.h>
36 #include <protocol/kad2/Constants.h>
37 #include <protocol/kad2/Client2Client/TCP.h>
38 #include <protocol/kad2/Client2Client/UDP.h>
40 #include <common/ClientVersion.h>
42 #include <tags/ClientTags.h>
44 #include <zlib.h> // Needed for inflateEnd
46 #include <common/Format.h> // Needed for CFormat
48 #include "SearchList.h" // Needed for CSearchList
49 #include "DownloadQueue.h" // Needed for CDownloadQueue
50 #include "UploadQueue.h" // Needed for CUploadQueue
51 #include "IPFilter.h" // Needed for CIPFilter
52 #include "ServerConnect.h" // Needed for CServerConnect
53 #include "ClientCredits.h" // Needed for CClientCredits
54 #include "ClientCreditsList.h" // Needed for CClientCreditsList
55 #include "Server.h" // Needed for CServer
56 #include "Preferences.h" // Needed for CPreferences
57 #include "MemFile.h" // Needed for CMemFile
58 #include "Packet.h" // Needed for CPacket
59 #include "Friend.h" // Needed for CFriend
60 #include "ClientList.h" // Needed for CClientList
61 #ifndef AMULE_DAEMON
62 #include "amuleDlg.h" // Needed for CamuleDlg
63 #include "CaptchaDialog.h" // Needed for CCaptchaDialog
64 #include "CaptchaGenerator.h"
65 #include "ChatWnd.h" // Needed for CChatWnd
66 #endif
67 #include "amule.h" // Needed for theApp
68 #include "PartFile.h" // Needed for CPartFile
69 #include "ClientTCPSocket.h" // Needed for CClientTCPSocket
70 #include "ListenSocket.h" // Needed for CListenSocket
71 #include "FriendList.h" // Needed for CFriendList
72 #include "Statistics.h" // Needed for theStats
73 #include "ClientUDPSocket.h"
74 #include "Logger.h"
75 #include "DataToText.h" // Needed for GetSoftName()
76 #include "GuiEvents.h" // Needed for Notify_
77 #include "ServerList.h" // For CServerList
79 #include "kademlia/kademlia/Kademlia.h"
80 #include "kademlia/kademlia/Prefs.h"
81 #include "kademlia/kademlia/Search.h"
82 #include "kademlia/kademlia/UDPFirewallTester.h"
83 #include "kademlia/routing/RoutingZone.h"
86 //#define __PACKET_DEBUG__
89 // some client testing variables
90 static wxString crash_name = wxT("[Invalid User Name]");
91 static wxString empty_name = wxT("[Empty User Name]");
93 // members of CUpDownClient
94 // which are used by down and uploading functions
97 CUpDownClient::CUpDownClient(CClientTCPSocket* sender)
99 #ifdef __DEBUG__
100 m_socket = NULL;
101 SetSocket(sender);
102 #else
103 m_socket = sender;
104 #endif
105 Init();
108 CUpDownClient::CUpDownClient(uint16 in_port, uint32 in_userid, uint32 in_serverip, uint16 in_serverport, CPartFile* in_reqfile, bool ed2kID, bool checkfriend)
110 m_socket = NULL;
111 Init();
112 m_nUserPort = in_port;
114 if(ed2kID && !IsLowID(in_userid)) {
115 SetUserIDHybrid( wxUINT32_SWAP_ALWAYS(in_userid) );
116 } else {
117 SetUserIDHybrid( in_userid);
120 //If highID and ED2K source, incoming ID and IP are equal..
121 //If highID and Kad source, incoming IP needs swap for the IP
123 if (!HasLowID()) {
124 if (ed2kID) {
125 m_nConnectIP = in_userid;
126 } else {
127 m_nConnectIP = wxUINT32_SWAP_ALWAYS(in_userid);
129 // Will be on right endianess now
130 m_FullUserIP = m_nConnectIP;
133 m_dwServerIP = in_serverip;
134 m_nServerPort = in_serverport;
135 SetRequestFile( in_reqfile );
136 ReGetClientSoft();
138 if (checkfriend) {
139 if ((m_Friend = theApp->friendlist->FindFriend(CMD4Hash(), m_dwUserIP, m_nUserPort)) != NULL){
140 m_Friend->LinkClient(this);
141 } else{
142 // avoid that an unwanted client instance keeps a friend slot
143 m_bFriendSlot = false;
149 void CUpDownClient::Init()
151 m_bAddNextConnect = false;
152 credits = NULL;
153 m_byChatstate = MS_NONE;
154 m_nKadState = KS_NONE;
155 m_nChatCaptchaState = CA_NONE;
156 m_cShowDR = 0;
157 m_reqfile = NULL; // No file required yet
158 m_nTransferredUp = 0;
159 m_cSendblock = 0;
160 m_cAsked = 0;
161 msReceivedPrev = 0;
162 kBpsDown = 0.0;
163 bytesReceivedCycle = 0;
164 m_nServerPort = 0;
165 m_iFileListRequested = 0;
166 m_dwLastUpRequest = 0;
167 m_bEmuleProtocol = false;
168 m_bCompleteSource = false;
169 m_bFriendSlot = false;
170 m_bCommentDirty = false;
171 m_bReaskPending = false;
172 m_bUDPPending = false;
173 m_nUserPort = 0;
174 m_nPartCount = 0;
175 m_dwLastAskedTime = 0;
176 m_nDownloadState = DS_NONE;
177 m_dwUploadTime = 0;
178 m_nTransferredDown = 0;
179 m_nUploadState = US_NONE;
180 m_dwLastBlockReceived = 0;
181 m_bUnicodeSupport = false;
183 m_fSentOutOfPartReqs = 0;
184 m_nCurQueueSessionPayloadUp = 0;
185 m_addedPayloadQueueSession = 0;
186 m_nUpDatarate = 0;
187 m_nSumForAvgUpDataRate = 0;
189 m_nRemoteQueueRank = 0;
190 m_nOldRemoteQueueRank = 0;
191 m_dwLastSourceRequest = 0;
192 m_dwLastSourceAnswer = 0;
193 m_dwLastAskedForSources = 0;
195 m_SecureIdentState = IS_UNAVAILABLE;
196 m_dwLastSignatureIP = 0;
198 m_byInfopacketsReceived = IP_NONE;
200 m_bIsHybrid = false;
201 m_bIsML = false;
202 m_Friend = NULL;
203 m_iRating = 0;
204 m_nCurSessionUp = 0;
205 m_clientSoft=SO_UNKNOWN;
207 m_bRemoteQueueFull = false;
208 m_HasValidHash = false;
209 SetWaitStartTime();
211 m_fHashsetRequesting = 0;
212 m_fSharedDirectories = 0;
213 m_lastPartAsked = 0xffff;
214 m_nUpCompleteSourcesCount= 0;
215 m_lastRefreshedDLDisplay = 0;
216 m_bHelloAnswerPending = false;
217 m_fSentCancelTransfer = 0;
218 m_Aggressiveness = 0;
219 m_LastFileRequest = 0;
221 m_clientState = CS_NEW;
223 ClearHelloProperties();
225 m_pReqFileAICHHash = NULL;
226 m_fSupportsAICH = 0;
227 m_fAICHRequested = 0;
228 m_fSupportsLargeFiles = 0;
229 m_fExtMultiPacket = 0;
230 m_fIsSpammer = 0;
232 m_dwUserIP = 0;
233 m_nConnectIP = 0;
234 m_dwServerIP = 0;
236 m_fNeedOurPublicIP = false;
237 m_bHashsetRequested = false;
239 m_nLastBlockOffset = 0;
241 m_uploadingfile = NULL;
243 m_OSInfo_sent = false;
245 /* Kad stuff */
246 SetBuddyID(NULL);
247 m_nBuddyIP = 0;
248 m_nBuddyPort = 0;
249 m_nUserIDHybrid = 0;
251 m_nSourceFrom = SF_NONE;
253 if (m_socket) {
254 amuleIPV4Address address;
255 m_socket->GetPeer(address);
256 SetIP(StringIPtoUint32(address.IPAddress()));
257 } else {
258 SetIP(0);
261 /* Statistics */
262 m_lastClientSoft = (uint32)(-1);
263 m_lastClientVersion = 0;
265 /* Creation time (for buddies timeout) */
266 m_nCreationTime = ::GetTickCount();
268 m_MaxBlockRequests = STANDARD_BLOCKS_REQUEST; // Safe starting amount
270 m_last_block_start = 0;
271 m_lastaverage = 0;
273 SetLastBuddyPingPongTime();
274 m_fRequestsCryptLayer = 0;
275 m_fSupportsCryptLayer = 0;
276 m_fRequiresCryptLayer = 0;
277 m_fSupportsSourceEx2 = 0;
278 m_fSupportsCaptcha = 0;
279 m_fDirectUDPCallback = 0;
280 m_dwDirectCallbackTimeout = 0;
282 m_hasbeenobfuscatinglately = false;
284 m_cCaptchasSent = 0;
285 m_cMessagesReceived = 0;
286 m_cMessagesSent = 0;
291 CUpDownClient::~CUpDownClient()
293 #ifdef __DEBUG__
294 if (!connection_reason.IsEmpty()) {
295 AddDebugLogLineN(logClient, wxT("Client to check for ") + connection_reason + wxT(" was deleted without connection."));
297 #endif
299 if (m_lastClientSoft == SO_UNKNOWN) {
300 theStats::RemoveUnknownClient();
301 } else if (m_lastClientSoft != (uint32)(-1)) {
302 theStats::RemoveKnownClient(m_lastClientSoft, m_lastClientVersion, m_lastOSInfo);
305 // Indicate that we are not anymore on stats
306 m_lastClientSoft = (uint32)(-1);
309 if (IsAICHReqPending()){
310 m_fAICHRequested = FALSE;
311 CAICHHashSet::ClientAICHRequestFailed(this);
314 //theApp->clientlist->RemoveClient(this, wxT("Destructing client object"));
316 if (m_Friend) {
317 m_Friend->UnLinkClient();
318 Notify_ChatRefreshFriend(m_Friend->GetIP(), m_Friend->GetPort(), wxEmptyString);
319 m_Friend = NULL;
322 // The socket should have been removed in Safe_Delete, but it
323 // doesn't hurt to have an extra check.
324 if (m_socket) {
325 m_socket->Safe_Delete();
326 // Paranoia
327 SetSocket(NULL);
331 ClearUploadBlockRequests();
332 ClearDownloadBlockRequests();
334 DeleteContents(m_WaitingPackets_list);
336 if (m_iRating>0 || !m_strComment.IsEmpty()) {
337 m_iRating = 0;
338 m_strComment.Clear();
339 if (m_reqfile) {
340 m_reqfile->UpdateFileRatingCommentAvail();
344 // Ensure that source-counts gets updated in case
345 // of a source not on the download-queue
346 SetRequestFile( NULL );
348 SetUploadFileID(NULL);
350 if (m_pReqFileAICHHash != NULL) {
351 delete m_pReqFileAICHHash;
352 m_pReqFileAICHHash = NULL;
356 void CUpDownClient::ClearHelloProperties()
358 m_nUDPPort = 0;
359 m_byUDPVer = 0;
360 m_byDataCompVer = 0;
361 m_byEmuleVersion = 0;
362 m_bySourceExchange1Ver = 0;
363 m_byAcceptCommentVer = 0;
364 m_byExtendedRequestsVer = 0;
365 m_byCompatibleClient = 0;
366 m_nKadPort = 0;
367 m_bySupportSecIdent = 0;
368 m_bSupportsPreview = 0;
369 m_nClientVersion = 0;
370 m_fSharedDirectories = 0;
371 m_bMultiPacket = 0;
372 m_fOsInfoSupport = 0;
373 m_fValueBasedTypeTags = 0;
374 SecIdentSupRec = 0;
375 m_byKadVersion = 0;
376 m_fRequestsCryptLayer = 0;
377 m_fSupportsCryptLayer = 0;
378 m_fRequiresCryptLayer = 0;
379 m_fSupportsSourceEx2 = 0;
380 m_fSupportsCaptcha = 0;
381 m_fDirectUDPCallback = 0;
384 bool CUpDownClient::ProcessHelloPacket(const byte* pachPacket, uint32 nSize)
386 const CMemFile data(pachPacket,nSize);
387 uint8 hashsize = data.ReadUInt8();
388 if ( 16 != hashsize ) {
390 * Hint: We can not accept other sizes here because:
391 * - the magic number is spread all over the source
392 * - the answer packet lacks the size field
394 throw wxString(wxT("Invalid Hello packet: Other userhash sizes than 16 are not implemented"));
396 // eMule 0.42: reset all client properties; a client may not send a particular emule tag any longer
397 ClearHelloProperties();
399 return ProcessHelloTypePacket(data);
402 void CUpDownClient::Safe_Delete()
404 // Because we are delaying the deletion, we might end up trying to delete
405 // it twice, however, this is normal and shouldn't trigger any failures
406 if ( m_clientState == CS_DYING ) {
407 return;
410 m_clientState = CS_DYING;
412 // Close the socket to avoid any more connections and related events
413 if ( m_socket ) {
414 m_socket->Safe_Delete();
415 // Paranoia
416 SetSocket(NULL);
419 // Schedule the client for deletion if we still have the clientlist
420 if ( theApp->clientlist ) {
421 theApp->clientlist->AddToDeleteQueue( this );
422 } else {
423 delete this;
428 bool CUpDownClient::ProcessHelloAnswer(const byte* pachPacket, uint32 nSize)
430 const CMemFile data(pachPacket,nSize);
431 bool bIsMule = ProcessHelloTypePacket(data);
432 m_bHelloAnswerPending = false;
433 return bIsMule;
436 bool CUpDownClient::ProcessHelloTypePacket(const CMemFile& data)
439 m_bIsHybrid = false;
440 m_bIsML = false;
441 m_fNoViewSharedFiles = 0;
442 m_bUnicodeSupport = false;
443 uint32 dwEmuleTags = 0;
445 CMD4Hash hash = data.ReadHash();
446 SetUserHash( hash );
447 SetUserIDHybrid( data.ReadUInt32() );
448 uint16 nUserPort = data.ReadUInt16(); // hmm clientport is sent twice - why?
449 uint32 tagcount = data.ReadUInt32();
450 for (uint32 i = 0;i < tagcount; i++){
451 CTag temptag(data, true);
452 switch(temptag.GetNameID()){
453 case CT_NAME:
454 m_Username = temptag.GetStr();
455 break;
457 case CT_VERSION:
458 m_nClientVersion = temptag.GetInt();
459 break;
461 case ET_MOD_VERSION:
462 if (temptag.IsStr()) {
463 m_strModVersion = temptag.GetStr();
464 } else if (temptag.IsInt()) {
465 m_strModVersion = wxString::Format(wxT("ModID=%u"), temptag.GetInt());
466 } else {
467 m_strModVersion = wxT("ModID=<Unknown>");
470 break;
472 case CT_PORT:
473 nUserPort = temptag.GetInt();
474 break;
476 case CT_EMULE_UDPPORTS:
477 // 16 KAD Port
478 // 16 UDP Port
479 SetKadPort((temptag.GetInt() >> 16) & 0xFFFF);
480 m_nUDPPort = temptag.GetInt() & 0xFFFF;
481 dwEmuleTags |= 1;
482 #ifdef __PACKET_DEBUG__
483 AddLogLineNS(CFormat(wxT("Hello type packet processing with eMule ports UDP=%i KAD=%i")) % m_nUDPPort % m_nKadPort);
484 #endif
485 break;
487 case CT_EMULE_BUDDYIP:
488 // 32 BUDDY IP
489 m_nBuddyIP = temptag.GetInt();
490 #ifdef __PACKET_DEBUG__
491 AddLogLineNS(CFormat(wxT("Hello type packet processing with eMule BuddyIP=%u (%s)")) % m_nBuddyIP % Uint32toStringIP(m_nBuddyIP));
492 #endif
493 break;
495 case CT_EMULE_BUDDYUDP:
496 // 16 --Reserved for future use--
497 // 16 BUDDY Port
498 m_nBuddyPort = (uint16)temptag.GetInt();
499 #ifdef __PACKET_DEBUG__
500 AddLogLineNS(CFormat(wxT("Hello type packet processing with eMule BuddyPort=%u")) % m_nBuddyPort);
501 #endif
502 break;
504 case CT_EMULE_MISCOPTIONS1: {
505 // 3 AICH Version (0 = not supported)
506 // 1 Unicode
507 // 4 UDP version
508 // 4 Data compression version
509 // 4 Secure Ident
510 // 4 Source Exchange
511 // 4 Ext. Requests
512 // 4 Comments
513 // 1 PeerCache supported
514 // 1 No 'View Shared Files' supported
515 // 1 MultiPacket
516 // 1 Preview
517 uint32 flags = temptag.GetInt();
518 m_fSupportsAICH = (flags >> (4*7+1)) & 0x07;
519 m_bUnicodeSupport = (flags >> 4*7) & 0x01;
520 m_byUDPVer = (flags >> 4*6) & 0x0f;
521 m_byDataCompVer = (flags >> 4*5) & 0x0f;
522 m_bySupportSecIdent = (flags >> 4*4) & 0x0f;
523 m_bySourceExchange1Ver = (flags >> 4*3) & 0x0f;
524 m_byExtendedRequestsVer = (flags >> 4*2) & 0x0f;
525 m_byAcceptCommentVer = (flags >> 4*1) & 0x0f;
526 m_fNoViewSharedFiles = (flags >> 1*2) & 0x01;
527 m_bMultiPacket = (flags >> 1*1) & 0x01;
528 m_fSupportsPreview = (flags >> 1*0) & 0x01;
529 dwEmuleTags |= 2;
530 #ifdef __PACKET_DEBUG__
531 AddLogLineNS(wxT("Hello type packet processing with eMule Misc Options:"));
532 AddLogLineNS(CFormat(wxT("m_byUDPVer = %i")) % m_byUDPVer);
533 AddLogLineNS(CFormat(wxT("m_byDataCompVer = %i")) % m_byDataCompVer);
534 AddLogLineNS(CFormat(wxT("m_bySupportSecIdent = %i")) % m_bySupportSecIdent);
535 AddLogLineNS(CFormat(wxT("m_bySourceExchangeVer = %i")) % m_bySourceExchange1Ver);
536 AddLogLineNS(CFormat(wxT("m_byExtendedRequestsVer = %i")) % m_byExtendedRequestsVer);
537 AddLogLineNS(CFormat(wxT("m_byAcceptCommentVer = %i")) % m_byAcceptCommentVer);
538 AddLogLineNS(CFormat(wxT("m_fNoViewSharedFiles = %i")) % m_fNoViewSharedFiles);
539 AddLogLineNS(CFormat(wxT("m_bMultiPacket = %i")) % m_bMultiPacket);
540 AddLogLineNS(CFormat(wxT("m_fSupportsPreview = %i")) % m_fSharedDirectories);
541 AddLogLineNS(wxT("That's all."));
542 #endif
543 SecIdentSupRec += 1;
544 break;
547 case CT_EMULE_MISCOPTIONS2:
548 // 19 Reserved
549 // 1 Direct UDP Callback supported and available
550 // 1 Supports ChatCaptchas
551 // 1 Supports SourceExachnge2 Packets, ignores SX1 Packet Version
552 // 1 Requires CryptLayer
553 // 1 Requests CryptLayer
554 // 1 Supports CryptLayer
555 // 1 Reserved (ModBit)
556 // 1 Ext Multipacket (Hash+Size instead of Hash)
557 // 1 Large Files (includes support for 64bit tags)
558 // 4 Kad Version - will go up to version 15 only (may need to add another field at some point in the future)
559 m_fDirectUDPCallback = (temptag.GetInt() >> 12) & 0x01;
560 m_fSupportsCaptcha = (temptag.GetInt() >> 11) & 0x01;
561 m_fSupportsSourceEx2 = (temptag.GetInt() >> 10) & 0x01;
562 m_fRequiresCryptLayer = (temptag.GetInt() >> 9) & 0x01;
563 m_fRequestsCryptLayer = (temptag.GetInt() >> 8) & 0x01;
564 m_fSupportsCryptLayer = (temptag.GetInt() >> 7) & 0x01;
565 // reserved 1
566 m_fExtMultiPacket = (temptag.GetInt() >> 5) & 0x01;
567 m_fSupportsLargeFiles = (temptag.GetInt() >> 4) & 0x01;
568 m_byKadVersion = (temptag.GetInt() >> 0) & 0x0f;
569 dwEmuleTags |= 8;
571 m_fRequestsCryptLayer &= m_fSupportsCryptLayer;
572 m_fRequiresCryptLayer &= m_fRequestsCryptLayer;
574 #ifdef __PACKET_DEBUG__
575 AddLogLineNS(wxT("Hello type packet processing with eMule Misc Options 2:"));
576 AddLogLineNS(CFormat(wxT(" m_fDirectUDPCallback = %i")) % m_fDirectUDPCallback);
577 AddLogLineNS(CFormat(wxT(" m_fSupportsCaptcha = %i")) % m_fSupportsCaptcha);
578 AddLogLineNS(CFormat(wxT(" m_fSupportsSourceEx2 = %i")) % m_fSupportsSourceEx2);
579 AddLogLineNS(CFormat(wxT(" m_fRequiresCryptLayer = %i")) % m_fRequiresCryptLayer);
580 AddLogLineNS(CFormat(wxT(" m_fRequestsCryptLayer = %i")) % m_fRequestsCryptLayer);
581 AddLogLineNS(CFormat(wxT(" m_fSupportsCryptLayer = %i")) % m_fSupportsCryptLayer);
582 AddLogLineNS(CFormat(wxT(" m_fExtMultiPacket = %i")) % m_fExtMultiPacket);
583 AddLogLineNS(CFormat(wxT(" m_fSupportsLargeFiles = %i")) % m_fSupportsLargeFiles);
584 AddLogLineNS(CFormat(wxT(" KadVersion = %u")) % m_byKadVersion);
585 AddLogLineNS(wxT("That's all."));
586 #endif
587 break;
589 // Special tag for Compat. Clients Misc options.
590 case CT_EMULECOMPAT_OPTIONS:
591 // 1 Operative System Info
592 // 1 Value-based-type int tags (experimental!)
593 m_fValueBasedTypeTags = (temptag.GetInt() >> 1*1) & 0x01;
594 m_fOsInfoSupport = (temptag.GetInt() >> 1*0) & 0x01;
595 break;
597 case CT_EMULE_VERSION:
598 // 8 Compatible Client ID
599 // 7 Mjr Version (Doesn't really matter..)
600 // 7 Min Version (Only need 0-99)
601 // 3 Upd Version (Only need 0-5)
602 // 7 Bld Version (Only need 0-99)
603 m_byCompatibleClient = (temptag.GetInt() >> 24);
604 m_nClientVersion = temptag.GetInt() & 0x00ffffff;
605 m_byEmuleVersion = 0x99;
606 m_fSharedDirectories = 1;
607 dwEmuleTags |= 4;
608 break;
612 m_nUserPort = nUserPort;
613 m_dwServerIP = data.ReadUInt32();
614 m_nServerPort = data.ReadUInt16();
615 // Hybrid now has an extra uint32.. What is it for?
616 // Also, many clients seem to send an extra 6? These are not eDonkeys or Hybrids..
617 if ( data.GetLength() - data.GetPosition() == sizeof(uint32) ) {
618 uint32 test = data.ReadUInt32();
619 /*if (test == 'KDLM') below kdlm is converted to ascii values.
620 This fixes a warning with gcc 3.4.
621 K=4b D=44 L=4c M=4d
623 if (test == 0x4b444c4d) { //if it's == "KDLM"
624 m_bIsML=true;
625 } else{
626 m_bIsHybrid = true;
627 m_fSharedDirectories = 1;
631 if (m_socket) {
632 amuleIPV4Address address;
633 m_socket->GetPeer(address);
634 SetIP(StringIPtoUint32(address.IPAddress()));
635 } else {
636 throw wxString(wxT("Huh, socket failure. Avoided crash this time."));
639 if (thePrefs::AddServersFromClient()) {
640 CServer* addsrv = new CServer(m_nServerPort, Uint32toStringIP(m_dwServerIP));
641 addsrv->SetListName(addsrv->GetAddress());
642 if (!theApp->AddServer(addsrv)) {
643 delete addsrv;
647 //(a)If this is a highID user, store the ID in the Hybrid format.
648 //(b)Some older clients will not send a ID, these client are HighID users that are not connected to a server.
649 //(c)Kad users with a *.*.*.0 IPs will look like a lowID user they are actually a highID user.. They can be detected easily
650 //because they will send a ID that is the same as their IP..
651 if(!HasLowID() || m_nUserIDHybrid == 0 || m_nUserIDHybrid == m_dwUserIP ) {
652 SetUserIDHybrid(wxUINT32_SWAP_ALWAYS(m_dwUserIP));
655 // get client credits
656 CClientCredits* pFoundCredits = theApp->clientcredits->GetCredit(m_UserHash);
657 if (credits == NULL){
658 credits = pFoundCredits;
659 if (!theApp->clientlist->ComparePriorUserhash(m_dwUserIP, m_nUserPort, pFoundCredits)){
660 AddDebugLogLineM( false, logClient, CFormat( wxT("Client: %s (%s) Banreason: Userhash changed (Found in TrackedClientsList)") ) % GetUserName() % GetFullIP() );
661 Ban();
663 } else if (credits != pFoundCredits){
664 // userhash change ok, however two hours "waittime" before it can be used
665 credits = pFoundCredits;
666 AddDebugLogLineM( false, logClient, CFormat( wxT("Client: %s (%s) Banreason: Userhash changed") ) % GetUserName() % GetFullIP() );
667 Ban();
670 if ((m_Friend = theApp->friendlist->FindFriend(m_UserHash, m_dwUserIP, m_nUserPort)) != NULL){
671 m_Friend->LinkClient(this);
672 } else{
673 // avoid that an unwanted client instance keeps a friend slot
674 SetFriendSlot(false);
678 ReGetClientSoft();
680 m_byInfopacketsReceived |= IP_EDONKEYPROTPACK;
682 // check if at least CT_EMULEVERSION was received, all other tags are optional
683 bool bIsMule = (dwEmuleTags & 0x04) == 0x04;
684 if (bIsMule) {
685 m_bEmuleProtocol = true;
686 m_byInfopacketsReceived |= IP_EMULEPROTPACK;
689 if( GetKadPort() ) {
690 Kademlia::CKademlia::Bootstrap(wxUINT32_SWAP_ALWAYS(GetIP()), GetKadPort(), GetKadVersion() > 1);
693 return bIsMule;
697 bool CUpDownClient::SendHelloPacket() {
699 if (m_socket == NULL){
700 wxFAIL;
701 return true;
704 // if IP is filtered, dont greet him but disconnect...
705 amuleIPV4Address address;
706 m_socket->GetPeer(address);
707 if ( theApp->ipfilter->IsFiltered(StringIPtoUint32(address.IPAddress()))) {
708 if (Disconnected(wxT("IPFilter"))) {
709 Safe_Delete();
710 return false;
712 return true;
715 CMemFile data(128);
716 data.WriteUInt8(16); // size of userhash
717 SendHelloTypePacket(&data);
719 CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_HELLO);
720 theStats::AddUpOverheadOther(packet->GetPacketSize());
721 SendPacket(packet,true);
722 m_bHelloAnswerPending = true;
723 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_HELLO to ") + GetFullIP() );
724 return true;
727 void CUpDownClient::SendMuleInfoPacket(bool bAnswer, bool OSInfo) {
729 if (m_socket == NULL){
730 wxFAIL;
731 return;
734 CPacket* packet = NULL;
735 CMemFile data;
737 data.WriteUInt8(CURRENT_VERSION_SHORT);
739 if (OSInfo) {
741 // Special MuleInfo packet for clients supporting it.
742 // This means aMule >= 2.0.0 and Hydranode
744 // Violently mark it as special Mule Info packet
745 // Sending this makes non-supporting-osinfo clients to refuse to read this
746 // packet. Anyway, this packet should NEVER get to non-supporting clients.
748 data.WriteUInt8(/*EMULE_PROTOCOL*/ 0xFF);
750 data.WriteUInt32(1); // One Tag (OS_INFO)
752 CTagString tag1(ET_OS_INFO,theApp->GetOSType());
753 tag1.WriteTagToFile(&data);
755 m_OSInfo_sent = true; // So we don't send it again
757 } else {
759 // Normal MuleInfo packet
761 // Kry - There's no point on upgrading to VBT tags here
762 // as no client supporting it uses mule info packet.
764 data.WriteUInt8(EMULE_PROTOCOL);
766 // Tag number
767 data.WriteUInt32(9);
769 CTagInt32 tag1(ET_COMPRESSION,1);
770 tag1.WriteTagToFile(&data);
771 CTagInt32 tag2(ET_UDPVER,4);
772 tag2.WriteTagToFile(&data);
773 CTagInt32 tag3(ET_UDPPORT, thePrefs::GetEffectiveUDPPort());
774 tag3.WriteTagToFile(&data);
775 CTagInt32 tag4(ET_SOURCEEXCHANGE,3);
776 tag4.WriteTagToFile(&data);
777 CTagInt32 tag5(ET_COMMENTS,1);
778 tag5.WriteTagToFile(&data);
779 CTagInt32 tag6(ET_EXTENDEDREQUEST,2);
780 tag6.WriteTagToFile(&data);
782 uint32 dwTagValue = (theApp->CryptoAvailable() ? 3 : 0);
783 // Kry - Needs the preview code from eMule
785 // set 'Preview supported' only if 'View Shared Files' allowed
786 if (thePrefs::CanSeeShares() != vsfaNobody) {
787 dwTagValue |= 128;
790 CTagInt32 tag7(ET_FEATURES, dwTagValue);
791 tag7.WriteTagToFile(&data);
793 CTagInt32 tag8(ET_COMPATIBLECLIENT,SO_AMULE);
794 tag8.WriteTagToFile(&data);
796 // Support for tag ET_MOD_VERSION
797 wxString mod_name(MOD_VERSION_LONG);
798 CTagString tag9(ET_MOD_VERSION, mod_name);
799 tag9.WriteTagToFile(&data);
800 // Maella end
804 packet = new CPacket(data, OP_EMULEPROT, (bAnswer ? OP_EMULEINFOANSWER : OP_EMULEINFO));
806 if (m_socket) {
807 theStats::AddUpOverheadOther(packet->GetPacketSize());
808 SendPacket(packet,true,true);
810 if (!bAnswer) {
811 if (!OSInfo) {
812 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_EMULEINFO to ") + GetFullIP() );
813 } else {
814 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_EMULEINFO/OS_INFO to ") + GetFullIP() );
816 } else {
817 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_EMULEINFOANSWER to ") + GetFullIP() );
822 bool CUpDownClient::ProcessMuleInfoPacket(const byte* pachPacket, uint32 nSize)
824 uint8 protocol_version;
826 const CMemFile data(pachPacket,nSize);
828 // The version number part of this packet will soon be useless since
829 // it is only able to go to v.99. Why the version is a uint8 and why
830 // it was not done as a tag like the eDonkey hello packet is not known.
831 // Therefore, sooner or later, we are going to have to switch over to
832 // using the eDonkey hello packet to set the version. No sense making
833 // a third value sent for versions.
834 uint8 mule_version = data.ReadUInt8();
835 protocol_version = data.ReadUInt8();
836 uint32 tagcount = data.ReadUInt32();
837 if (protocol_version == 0xFF) {
838 // OS Info supporting clients sending a recycled Mule info packet
839 for (uint32 i = 0;i < tagcount; i++){
840 CTag temptag(data, true);
841 switch(temptag.GetNameID()){
842 case ET_OS_INFO:
843 // Special tag, only supporting clients (aMule/Hydranode)
844 // It was recycled from a mod's tag, so if the other side
845 // is not supporting OS Info, we're seriously fucked up :)
846 m_sClientOSInfo = temptag.GetStr();
848 // If we didn't send our OSInfo to this client, just send it
849 if (!m_OSInfo_sent) {
850 SendMuleInfoPacket(false,true);
853 UpdateStats();
855 break;
857 // Your ad... er... I mean TAG, here
859 default:
860 break;
863 } else {
864 // Old eMule sending tags
865 m_byCompatibleClient = 0;
866 m_byEmuleVersion = mule_version;
868 if( m_byEmuleVersion == 0x2B ) {
869 m_byEmuleVersion = 0x22;
872 if (!(m_bEmuleProtocol = (protocol_version == EMULE_PROTOCOL))) {
873 return false;
876 for (uint32 i = 0;i < tagcount; i++){
877 CTag temptag(data, false);
878 switch(temptag.GetNameID()){
879 case ET_COMPRESSION:
880 // Bits 31- 8: 0 - reserved
881 // Bits 7- 0: data compression version
882 m_byDataCompVer = temptag.GetInt();
883 break;
885 case ET_UDPPORT:
886 // Bits 31-16: 0 - reserved
887 // Bits 15- 0: UDP port
888 m_nUDPPort = temptag.GetInt();
889 break;
891 case ET_UDPVER:
892 // Bits 31- 8: 0 - reserved
893 // Bits 7- 0: UDP protocol version
894 m_byUDPVer = temptag.GetInt();
895 break;
897 case ET_SOURCEEXCHANGE:
898 // Bits 31- 8: 0 - reserved
899 // Bits 7- 0: source exchange protocol version
900 m_bySourceExchange1Ver = temptag.GetInt();
901 break;
903 case ET_COMMENTS:
904 // Bits 31- 8: 0 - reserved
905 // Bits 7- 0: comments version
906 m_byAcceptCommentVer = temptag.GetInt();
907 break;
909 case ET_EXTENDEDREQUEST:
910 // Bits 31- 8: 0 - reserved
911 // Bits 7- 0: extended requests version
912 m_byExtendedRequestsVer = temptag.GetInt();
913 break;
915 case ET_COMPATIBLECLIENT:
916 // Bits 31- 8: 0 - reserved
917 // Bits 7- 0: compatible client ID
918 m_byCompatibleClient = temptag.GetInt();
919 break;
921 case ET_FEATURES:
922 // Bits 31- 8: 0 - reserved
923 // Bit 7: Preview
924 // Bit 6- 0: secure identification
925 m_bySupportSecIdent = temptag.GetInt() & 3;
926 m_bSupportsPreview = (temptag.GetInt() & 128) > 0;
927 SecIdentSupRec += 2;
928 break;
930 case ET_MOD_VERSION:
931 if (temptag.IsStr()) {
932 m_strModVersion = temptag.GetStr();
933 } else if (temptag.IsInt()) {
934 m_strModVersion = wxString::Format(wxT("ModID=%u"), temptag.GetInt());
935 } else {
936 m_strModVersion = wxT("ModID=<Unknown>");
939 break;
941 default:
942 AddDebugLogLineM( false, logPacketErrors,
943 CFormat( wxT("Unknown Mule tag (%s) from client: %s") )
944 % temptag.GetFullInfo()
945 % GetClientFullInfo()
948 break;
952 if( m_byDataCompVer == 0 ){
953 m_bySourceExchange1Ver = 0;
954 m_byExtendedRequestsVer = 0;
955 m_byAcceptCommentVer = 0;
956 m_nUDPPort = 0;
959 //implicitly supported options by older clients
960 //in the future do not use version to guess about new features
961 if(m_byEmuleVersion < 0x25 && m_byEmuleVersion > 0x22) {
962 m_byUDPVer = 1;
965 if(m_byEmuleVersion < 0x25 && m_byEmuleVersion > 0x21) {
966 m_bySourceExchange1Ver = 1;
969 if(m_byEmuleVersion == 0x24) {
970 m_byAcceptCommentVer = 1;
973 // Shared directories are requested from eMule 0.28+ because eMule 0.27 has a bug in
974 // the OP_ASKSHAREDFILESDIR handler, which does not return the shared files for a
975 // directory which has a trailing backslash.
976 if(m_byEmuleVersion >= 0x28 && !m_bIsML) {// MLdonkey currently does not support shared directories
977 m_fSharedDirectories = 1;
980 ReGetClientSoft();
982 m_byInfopacketsReceived |= IP_EMULEPROTPACK;
985 return (protocol_version == 0xFF); // This was a OS_Info?
989 void CUpDownClient::SendHelloAnswer()
991 if (m_socket == NULL){
992 wxFAIL;
993 return;
996 CMemFile data(128);
997 SendHelloTypePacket(&data);
998 CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_HELLOANSWER);
999 theStats::AddUpOverheadOther(packet->GetPacketSize());
1000 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_HELLOANSWER to ") + GetFullIP() );
1001 SendPacket(packet,true);
1005 void CUpDownClient::SendHelloTypePacket(CMemFile* data)
1007 data->WriteHash(thePrefs::GetUserHash());
1008 data->WriteUInt32(theApp->GetID());
1009 data->WriteUInt16(thePrefs::GetPort());
1011 uint32 tagcount = 6;
1013 if( theApp->clientlist->GetBuddy() && theApp->IsFirewalled() ) {
1014 tagcount += 2;
1016 tagcount ++; // eMule misc flags 2 (kad version)
1018 #ifdef __SVN__
1019 // Kry - This is the tagcount!!! Be sure to update it!!
1020 // Last update: CT_EMULECOMPAT_OPTIONS included
1021 data->WriteUInt32(tagcount + 1);
1022 #else
1023 data->WriteUInt32(tagcount); // NO MOD_VERSION
1024 #endif
1027 CTagString tagname(CT_NAME,thePrefs::GetUserNick());
1028 tagname.WriteTagToFile(data, utf8strRaw);
1030 CTagVarInt tagversion(CT_VERSION, EDONKEYVERSION, GetVBTTags() ? 0 : 32);
1031 tagversion.WriteTagToFile(data);
1032 // eMule UDP Ports
1034 uint32 kadUDPPort = 0;
1036 if(Kademlia::CKademlia::IsConnected()) {
1037 if (Kademlia::CKademlia::GetPrefs()->GetExternalKadPort() != 0 && Kademlia::CKademlia::GetPrefs()->GetUseExternKadPort() && Kademlia::CUDPFirewallTester::IsVerified()) {
1038 kadUDPPort = Kademlia::CKademlia::GetPrefs()->GetExternalKadPort();
1039 } else {
1040 kadUDPPort = Kademlia::CKademlia::GetPrefs()->GetInternKadPort();
1044 CTagVarInt tagUdpPorts(CT_EMULE_UDPPORTS, (kadUDPPort << 16) | ((uint32)thePrefs::GetEffectiveUDPPort()), GetVBTTags() ? 0 : 32);
1045 tagUdpPorts.WriteTagToFile(data);
1047 if( theApp->clientlist->GetBuddy() && theApp->IsFirewalled() ) {
1048 CTagVarInt tagBuddyIP(CT_EMULE_BUDDYIP, theApp->clientlist->GetBuddy()->GetIP(), GetVBTTags() ? 0 : 32);
1049 tagBuddyIP.WriteTagToFile(data);
1051 CTagVarInt tagBuddyPort(CT_EMULE_BUDDYUDP,
1052 // ( RESERVED )
1053 ((uint32)theApp->clientlist->GetBuddy()->GetUDPPort() )
1054 , GetVBTTags() ? 0 : 32);
1055 tagBuddyPort.WriteTagToFile(data);
1058 // aMule Version
1059 CTagVarInt tagMuleVersion(CT_EMULE_VERSION,
1060 (SO_AMULE << 24) |
1061 make_full_ed2k_version(VERSION_MJR, VERSION_MIN, VERSION_UPDATE)
1062 // | (RESERVED )
1063 , GetVBTTags() ? 0 : 32);
1064 tagMuleVersion.WriteTagToFile(data);
1067 // eMule Misc. Options #1
1068 const uint32 uUdpVer = 4;
1069 const uint32 uDataCompVer = 1;
1070 const uint32 uSupportSecIdent = theApp->CryptoAvailable() ? 3 : 0;
1071 const uint32 uSourceExchangeVer = 3;
1072 const uint32 uExtendedRequestsVer = 2;
1073 const uint32 uAcceptCommentVer = 1;
1074 const uint32 uNoViewSharedFiles = (thePrefs::CanSeeShares() == vsfaNobody) ? 1 : 0; // for backward compatibility this has to be a 'negative' flag
1075 const uint32 uMultiPacket = 1;
1076 const uint32 uSupportPreview = 0; // No network preview at all.
1077 const uint32 uPeerCache = 0; // No peercache for aMule, baby
1078 const uint32 uUnicodeSupport = 1;
1079 const uint32 nAICHVer = 1; // AICH is ENABLED right now.
1081 CTagVarInt tagMisOptions(CT_EMULE_MISCOPTIONS1,
1082 (nAICHVer << ((4*7)+1)) |
1083 (uUnicodeSupport << 4*7) |
1084 (uUdpVer << 4*6) |
1085 (uDataCompVer << 4*5) |
1086 (uSupportSecIdent << 4*4) |
1087 (uSourceExchangeVer << 4*3) |
1088 (uExtendedRequestsVer << 4*2) |
1089 (uAcceptCommentVer << 4*1) |
1090 (uPeerCache << 1*3) |
1091 (uNoViewSharedFiles << 1*2) |
1092 (uMultiPacket << 1*1) |
1093 (uSupportPreview << 1*0)
1094 , GetVBTTags() ? 0 : 32);
1095 tagMisOptions.WriteTagToFile(data);
1097 // eMule Misc. Options #2
1098 const uint32 uKadVersion = KADEMLIA_VERSION;
1099 const uint32 uSupportLargeFiles = 1;
1100 const uint32 uExtMultiPacket = 1;
1101 const uint32 uReserved = 0; // mod bit
1102 const uint32 uSupportsCryptLayer = thePrefs::IsClientCryptLayerSupported() ? 1 : 0;
1103 const uint32 uRequestsCryptLayer = thePrefs::IsClientCryptLayerRequested() ? 1 : 0;
1104 const uint32 uRequiresCryptLayer = thePrefs::IsClientCryptLayerRequired() ? 1 : 0;
1105 const uint32 uSupportsSourceEx2 = 1;
1106 #ifdef AMULE_DAEMON
1107 // captcha for daemon/remotegui not supported for now
1108 const uint32 uSupportsCaptcha = 0;
1109 #else
1110 const uint32 uSupportsCaptcha = 1;
1111 #endif
1112 // direct callback is only possible if connected to kad, tcp firewalled and verified UDP open (for example on a full cone NAT)
1113 const uint32 uDirectUDPCallback = (Kademlia::CKademlia::IsRunning() && Kademlia::CKademlia::IsFirewalled()
1114 && !Kademlia::CUDPFirewallTester::IsFirewalledUDP(true) && Kademlia::CUDPFirewallTester::IsVerified()) ? 1 : 0;
1116 CTagVarInt tagMisOptions2(CT_EMULE_MISCOPTIONS2,
1117 // (RESERVED )
1118 (uDirectUDPCallback << 12) |
1119 (uSupportsCaptcha << 11) |
1120 (uSupportsSourceEx2 << 10) |
1121 (uRequiresCryptLayer << 9) |
1122 (uRequestsCryptLayer << 8) |
1123 (uSupportsCryptLayer << 7) |
1124 (uReserved << 6) |
1125 (uExtMultiPacket << 5) |
1126 (uSupportLargeFiles << 4) |
1127 (uKadVersion << 0)
1128 , GetVBTTags() ? 0 : 32 );
1129 tagMisOptions2.WriteTagToFile(data);
1131 const uint32 nOSInfoSupport = 1; // We support OS_INFO
1132 const uint32 nValueBasedTypeTags = 0; // Experimental, disabled
1134 CTagVarInt tagMisCompatOptions(CT_EMULECOMPAT_OPTIONS,
1135 (nValueBasedTypeTags << 1*1) |
1136 (nOSInfoSupport << 1*0)
1137 , GetVBTTags() ? 0 : 32);
1139 tagMisCompatOptions.WriteTagToFile(data);
1141 #ifdef __SVN__
1142 wxString mod_name(MOD_VERSION_LONG);
1143 CTagString tagModName(ET_MOD_VERSION, mod_name);
1144 tagModName.WriteTagToFile(data);
1145 #endif
1147 uint32 dwIP = 0;
1148 uint16 nPort = 0;
1149 if (theApp->IsConnectedED2K()) {
1150 dwIP = theApp->serverconnect->GetCurrentServer()->GetIP();
1151 nPort = theApp->serverconnect->GetCurrentServer()->GetPort();
1153 data->WriteUInt32(dwIP);
1154 data->WriteUInt16(nPort);
1158 void CUpDownClient::ProcessMuleCommentPacket(const byte* pachPacket, uint32 nSize)
1160 if (!m_reqfile) {
1161 throw CInvalidPacket(wxT("Comment packet for unknown file"));
1164 if (!m_reqfile->IsPartFile()) {
1165 throw CInvalidPacket(wxT("Comment packet for completed file"));
1168 const CMemFile data(pachPacket, nSize);
1170 uint8 rating = data.ReadUInt8();
1171 if (rating > 5) {
1172 AddDebugLogLineM( false, logClient, wxString(wxT("Invalid Rating for file '")) << m_clientFilename << wxT("' received: ") << rating);
1173 m_iRating = 0;
1174 } else {
1175 m_iRating = rating;
1176 AddDebugLogLineM( false, logClient, wxString(wxT("Rating for file '")) << m_clientFilename << wxT("' received: ") << m_iRating);
1179 // The comment is unicoded, with a uin32 len and safe read
1180 // (won't break if string size is < than advertised len)
1181 // Truncated to MAXFILECOMMENTLEN size
1182 m_strComment = data.ReadString((GetUnicodeSupport() != utf8strNone), 4 /* bytes (it's a uint32)*/, true).Left(MAXFILECOMMENTLEN);
1184 AddDebugLogLineM( false, logClient, wxString(wxT("Description for file '")) << m_clientFilename << wxT("' received: ") << m_strComment);
1186 // Update file rating
1187 m_reqfile->UpdateFileRatingCommentAvail();
1191 void CUpDownClient::ClearDownloadBlockRequests()
1194 std::list<Requested_Block_Struct*>::iterator it = m_DownloadBlocks_list.begin();
1195 for (; it != m_DownloadBlocks_list.end(); ++it) {
1196 Requested_Block_Struct* cur_block = *it;
1198 if (m_reqfile){
1199 m_reqfile->RemoveBlockFromList(cur_block->StartOffset, cur_block->EndOffset);
1202 delete cur_block;
1205 m_DownloadBlocks_list.clear();
1209 std::list<Pending_Block_Struct*>::iterator it = m_PendingBlocks_list.begin();
1210 for (; it != m_PendingBlocks_list.end(); ++it) {
1211 Pending_Block_Struct* pending = *it;
1213 if (m_reqfile) {
1214 m_reqfile->RemoveBlockFromList(pending->block->StartOffset, pending->block->EndOffset);
1217 delete pending->block;
1218 // Not always allocated
1219 if (pending->zStream){
1220 inflateEnd(pending->zStream);
1221 delete pending->zStream;
1224 delete pending;
1227 m_PendingBlocks_list.clear();
1232 bool CUpDownClient::Disconnected(const wxString& strReason, bool bFromSocket)
1234 //wxASSERT(theApp->clientlist->IsValidClient(this));
1236 // was this a direct callback?
1237 if (m_dwDirectCallbackTimeout != 0) {
1238 theApp->clientlist->RemoveDirectCallback(this);
1239 m_dwDirectCallbackTimeout = 0;
1240 theApp->clientlist->AddDeadSource(this);
1241 AddDebugLogLineM(false, logClient, wxT("Direct callback failed to client ") + GetUserHash().Encode() + wxT(" on ip ") + GetFullIP());
1244 if (GetKadState() == KS_QUEUED_FWCHECK_UDP || GetKadState() == KS_CONNECTING_FWCHECK_UDP) {
1245 Kademlia::CUDPFirewallTester::SetUDPFWCheckResult(false, true, wxUINT32_SWAP_ALWAYS(GetConnectIP()), 0); // inform the tester that this test was cancelled
1246 } else if (GetKadState() == KS_FWCHECK_UDP) {
1247 Kademlia::CUDPFirewallTester::SetUDPFWCheckResult(false, false, wxUINT32_SWAP_ALWAYS(GetConnectIP()), 0); // inform the tester that this test has failed
1248 // } else if (GetKadState() == KS_CONNECTED_BUDDY) {
1249 // AddDebugLogLineM(false, logClient, wxT("Buddy client disconnected - ") + strReason);
1252 //If this is a KAD client object, just delete it!
1253 SetKadState(KS_NONE);
1255 if (GetUploadState() == US_UPLOADING) {
1256 theApp->uploadqueue->RemoveFromUploadQueue(this);
1259 if (GetDownloadState() == DS_DOWNLOADING) {
1260 SetDownloadState(DS_ONQUEUE);
1261 } else{
1262 // ensure that all possible block requests are removed from the partfile
1263 ClearDownloadBlockRequests();
1265 if ( GetDownloadState() == DS_CONNECTED ){
1266 theApp->clientlist->AddDeadSource(this);
1267 theApp->downloadqueue->RemoveSource(this);
1271 // we had still an AICH request pending, handle it
1272 if (IsAICHReqPending()){
1273 m_fAICHRequested = FALSE;
1274 CAICHHashSet::ClientAICHRequestFailed(this);
1277 // The remote client does not have to answer with OP_HASHSETANSWER *immediatly*
1278 // after we've sent OP_HASHSETREQUEST. It may occure that a (buggy) remote client
1279 // is sending use another OP_FILESTATUS which would let us change to DL-state to DS_ONQUEUE.
1280 if (((GetDownloadState() == DS_REQHASHSET) || m_fHashsetRequesting) && (m_reqfile)) {
1281 m_reqfile->SetHashSetNeeded(true);
1284 //check if this client is needed in any way, if not delete it
1285 bool bDelete = true;
1286 switch(m_nUploadState){
1287 case US_ONUPLOADQUEUE:
1288 bDelete = false;
1289 break;
1291 switch(m_nDownloadState){
1292 case DS_ONQUEUE:
1293 case DS_TOOMANYCONNS:
1294 case DS_NONEEDEDPARTS:
1295 case DS_LOWTOLOWIP:
1296 bDelete = false;
1299 switch(m_nUploadState){
1300 case US_CONNECTING:
1301 case US_WAITCALLBACK:
1302 case US_ERROR:
1303 theApp->clientlist->AddDeadSource(this);
1304 bDelete = true;
1306 switch(m_nDownloadState){
1307 case DS_CONNECTING:
1308 case DS_WAITCALLBACK:
1309 case DS_ERROR:
1310 case DS_BANNED:
1311 theApp->clientlist->AddDeadSource(this);
1312 bDelete = true;
1316 if (GetChatState() != MS_NONE){
1317 bDelete = false;
1318 m_pendingMessage.Clear();
1319 Notify_ChatConnResult(false,GUI_ID(GetIP(),GetUserPort()),wxEmptyString);
1322 if (!bFromSocket && m_socket){
1323 wxASSERT (theApp->listensocket->IsValidSocket(m_socket));
1324 m_socket->Safe_Delete();
1327 SetSocket(NULL);
1329 if (m_iFileListRequested){
1330 AddLogLineM( false, CFormat(_("Failed to retrieve shared files from user '%s'")) % GetUserName() );
1331 m_iFileListRequested = 0;
1334 Notify_ClientCtrlRefreshClient( this );
1336 if (bDelete) {
1337 if (m_Friend) {
1338 // Remove the friend linkage
1339 Notify_ChatRefreshFriend(m_Friend->GetIP(), m_Friend->GetPort(), wxEmptyString);
1341 AddDebugLogLineM( false, logClient, wxString() <<
1342 wxT("--- Deleted client \"") << GetClientFullInfo() <<
1343 wxT("\"; Reason was ") << strReason );
1344 } else {
1345 AddDebugLogLineM( false, logClient, wxString() <<
1346 wxT("--- Disconnected client \"") << GetClientFullInfo() <<
1347 wxT("\"; Reason was ") << strReason );
1348 m_fHashsetRequesting = 0;
1349 SetSentCancelTransfer(0);
1350 m_bHelloAnswerPending = false;
1351 m_fSentOutOfPartReqs = 0;
1354 return bDelete;
1357 //Returned bool is not if the TryToConnect is successful or not..
1358 //false means the client was deleted!
1359 //true means the client was not deleted!
1360 bool CUpDownClient::TryToConnect(bool bIgnoreMaxCon)
1362 // Kad reviewed
1363 if (theApp->listensocket->TooManySockets() && !bIgnoreMaxCon ) {
1364 if (!(m_socket && m_socket->IsConnected())) {
1365 if(Disconnected(wxT("Too many connections"))) {
1366 Safe_Delete();
1367 return false;
1369 return true;
1373 // Do not try to connect to source which are incompatible with our encryption setting (one requires it, and the other one doesn't supports it)
1374 if ( (RequiresCryptLayer() && !thePrefs::IsClientCryptLayerSupported()) || (thePrefs::IsClientCryptLayerRequired() && !SupportsCryptLayer()) ){
1375 if(Disconnected(wxT("CryptLayer-Settings (Obfuscation) incompatible"))){
1376 Safe_Delete();
1377 return false;
1378 } else {
1379 return true;
1383 // Ipfilter check
1384 uint32 uClientIP = GetIP();
1385 if (uClientIP == 0 && !HasLowID()) {
1386 uClientIP = wxUINT32_SWAP_ALWAYS(m_nUserIDHybrid);
1389 if (uClientIP) {
1390 // Although we filter all received IPs (server sources, source exchange) and all incomming connection attempts,
1391 // we do have to filter outgoing connection attempts here too, because we may have updated the ip filter list
1392 if (theApp->ipfilter->IsFiltered(uClientIP)) {
1393 AddDebugLogLineM(false, logIPFilter, CFormat(wxT("Filtered ip %u (%s) on TryToConnect\n")) % uClientIP % Uint32toStringIP(uClientIP));
1394 if (Disconnected(wxT("IPFilter"))) {
1395 Safe_Delete();
1396 return false;
1397 } else {
1398 return true;
1402 // for safety: check again whether that IP is banned
1403 if (theApp->clientlist->IsBannedClient(uClientIP)) {
1404 AddDebugLogLineM(false, logClient, wxT("Refused to connect to banned client ") + Uint32toStringIP(uClientIP));
1405 if (Disconnected(wxT("Banned IP"))) {
1406 Safe_Delete();
1407 return false;
1409 return true;
1413 if (GetKadState() == KS_QUEUED_FWCHECK) {
1414 SetKadState(KS_CONNECTING_FWCHECK);
1415 } else if (GetKadState() == KS_QUEUED_FWCHECK_UDP) {
1416 SetKadState(KS_CONNECTING_FWCHECK_UDP);
1419 if ( HasLowID() ) {
1420 if (!theApp->DoCallback(this)) {
1421 //We cannot do a callback!
1422 if (GetDownloadState() == DS_CONNECTING) {
1423 SetDownloadState(DS_LOWTOLOWIP);
1424 } else if (GetDownloadState() == DS_REQHASHSET) {
1425 SetDownloadState(DS_ONQUEUE);
1426 m_reqfile->SetHashSetNeeded(true);
1428 if (GetUploadState() == US_CONNECTING) {
1429 if(Disconnected(wxT("LowID->LowID and US_CONNECTING"))) {
1430 Safe_Delete();
1431 return false;
1434 return true;
1437 //We already know we are not firewalled here as the above condition already detected LowID->LowID and returned.
1438 //If ANYTHING changes with the "if(!theApp->DoCallback(this))" above that will let you fall through
1439 //with the condition that the source is firewalled and we are firewalled, we must
1440 //recheck it before the this check..
1441 if (HasValidBuddyID() && !GetBuddyIP() && !GetBuddyPort() && !theApp->serverconnect->IsLocalServer(GetServerIP(), GetServerPort())
1442 && !(SupportsDirectUDPCallback() && thePrefs::GetEffectiveUDPPort() != 0)) {
1443 //This is a Kad firewalled source that we want to do a special callback because it has no buddyIP or buddyPort.
1444 if( Kademlia::CKademlia::IsConnected() ) {
1445 //We are connect to Kad
1446 if( Kademlia::CKademlia::GetPrefs()->GetTotalSource() > 0 || Kademlia::CSearchManager::AlreadySearchingFor(Kademlia::CUInt128(GetBuddyID()))) {
1447 //There are too many source lookups already or we are already searching this key.
1448 SetDownloadState(DS_TOOMANYCONNSKAD);
1449 return true;
1455 if (!m_socket || !m_socket->IsConnected()) {
1456 if (m_socket) {
1457 m_socket->Safe_Delete();
1459 m_socket = new CClientTCPSocket(this, thePrefs::GetProxyData());
1460 } else {
1461 ConnectionEstablished();
1462 return true;
1466 if (HasLowID() && SupportsDirectUDPCallback() && thePrefs::GetEffectiveUDPPort() != 0 && GetConnectIP() != 0) { // LOWID with DirectCallback
1467 if (m_dwDirectCallbackTimeout != 0) {
1468 AddDebugLogLineM(false, logClient, wxT("ERROR: Trying Direct UDP Callback while already trying to connect to client ") + GetUserHash().Encode());
1469 return true; // We're already trying a direct connection to this client
1471 // a direct callback is possible - since no other parties are involved and only one additional packet overhead
1472 // is used we basically handle it like a normal connection try, no restrictions apply
1473 // we already check above with !theApp->DoCallback(this) if any callback is possible at all
1474 m_dwDirectCallbackTimeout = ::GetTickCount() + SEC2MS(45);
1475 theApp->clientlist->AddDirectCallbackClient(this);
1476 // TODO LOGREMOVE
1477 AddDebugLogLineM(false, logClient, wxString::Format(wxT("Direct Callback on port %u to client "), GetKadPort()) + GetUserHash().Encode());
1479 CMemFile data;
1480 data.WriteUInt16(thePrefs::GetPort()); // needs to know our port
1481 data.WriteHash(thePrefs::GetUserHash()); // and userhash
1482 // our connection settings
1483 data.WriteUInt8(Kademlia::CPrefs::GetMyConnectOptions(true, false));
1484 AddDebugLogLineM(false, logClientUDP, wxT("Sending OP_DIRECTCALLBACKREQ to ") + Uint32_16toStringIP_Port(GetConnectIP(), GetKadPort()));
1485 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_DIRECTCALLBACKREQ);
1486 theStats::AddUpOverheadOther(packet->GetPacketSize());
1487 theApp->clientudp->SendPacket(packet, GetConnectIP(), GetKadPort(), ShouldReceiveCryptUDPPackets(), GetUserHash().GetHash(), false, 0);
1488 } else if (HasLowID()) { // LOWID
1489 if (GetDownloadState() == DS_CONNECTING) {
1490 SetDownloadState(DS_WAITCALLBACK);
1492 if (GetUploadState() == US_CONNECTING) {
1493 if(Disconnected(wxT("LowID and US_CONNECTING"))) {
1494 Safe_Delete();
1495 return false;
1497 return true;
1500 if (theApp->serverconnect->IsLocalServer(m_dwServerIP,m_nServerPort)) {
1501 CMemFile data;
1502 // AFAICS, this id must be reversed to be sent to clients
1503 // But if I reverse it, we do a serve violation ;)
1504 data.WriteUInt32(m_nUserIDHybrid);
1505 CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_CALLBACKREQUEST);
1506 theStats::AddUpOverheadServer(packet->GetPacketSize());
1507 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_CALLBACKREQUEST to ") + GetFullIP());
1508 theApp->serverconnect->SendPacket(packet);
1509 SetDownloadState(DS_WAITCALLBACK);
1510 } else {
1511 if (GetUploadState() == US_NONE && (!GetRemoteQueueRank() || m_bReaskPending)) {
1513 if( !HasValidBuddyID() ) {
1514 theApp->downloadqueue->RemoveSource(this);
1515 if (Disconnected(wxT("LowID and US_NONE and QR=0"))) {
1516 Safe_Delete();
1517 return false;
1519 return true;
1522 if( !Kademlia::CKademlia::IsConnected() ) {
1523 //We are not connected to Kad and this is a Kad Firewalled source..
1524 theApp->downloadqueue->RemoveSource(this);
1525 if(Disconnected(wxT("Kad Firewalled source but not connected to Kad."))) {
1526 Safe_Delete();
1527 return false;
1529 return true;
1532 if( GetDownloadState() == DS_WAITCALLBACK ) {
1533 if( GetBuddyIP() && GetBuddyPort()) {
1534 CMemFile bio(34);
1535 bio.WriteUInt128(Kademlia::CUInt128(GetBuddyID()));
1536 bio.WriteUInt128(Kademlia::CUInt128(m_reqfile->GetFileHash().GetHash()));
1537 bio.WriteUInt16(thePrefs::GetPort());
1538 CPacket* packet = new CPacket(bio, OP_KADEMLIAHEADER, KADEMLIA_CALLBACK_REQ);
1539 // eMule FIXME: We dont know which kadversion the buddy has, so we need to send unencrypted
1540 theApp->clientudp->SendPacket(packet, GetBuddyIP(), GetBuddyPort(), false, NULL, true, 0);
1541 AddDebugLogLineM(false,logLocalClient, wxString::Format(wxT("KADEMLIA_CALLBACK_REQ (%i) to"),packet->GetPacketSize()) + GetFullIP());
1542 theStats::AddUpOverheadKad(packet->GetRealPacketSize());
1543 SetDownloadState(DS_WAITCALLBACKKAD);
1544 } else {
1545 AddLogLineN(_("Searching buddy for lowid connection"));
1546 //Create search to find buddy.
1547 Kademlia::CSearch *findSource = new Kademlia::CSearch;
1548 findSource->SetSearchTypes(Kademlia::CSearch::FINDSOURCE);
1549 findSource->SetTargetID(Kademlia::CUInt128(GetBuddyID()));
1550 findSource->AddFileID(Kademlia::CUInt128(m_reqfile->GetFileHash().GetHash()));
1551 if(Kademlia::CSearchManager::StartSearch(findSource)) {
1552 //Started lookup..
1553 SetDownloadState(DS_WAITCALLBACKKAD);
1554 } else {
1555 //This should never happen..
1556 wxFAIL;
1560 } else {
1561 if (GetDownloadState() == DS_WAITCALLBACK) {
1562 m_bReaskPending = true;
1563 SetDownloadState(DS_ONQUEUE);
1567 } else { // HIGHID
1568 if (!Connect()) {
1569 return false;
1572 return true;
1575 bool CUpDownClient::Connect()
1577 m_hasbeenobfuscatinglately = false;
1579 if (!m_socket->IsOk()) {
1580 // Enable or disable crypting based on our and the remote clients preference
1581 if (HasValidHash() && SupportsCryptLayer() && thePrefs::IsClientCryptLayerSupported() && (RequestsCryptLayer() || thePrefs::IsClientCryptLayerRequested())){
1582 m_socket->SetConnectionEncryption(true, GetUserHash().GetHash(), false);
1583 } else {
1584 m_socket->SetConnectionEncryption(false, NULL, false);
1586 amuleIPV4Address tmp;
1587 tmp.Hostname(GetConnectIP());
1588 tmp.Service(GetUserPort());
1589 AddDebugLogLineM(false, logClient, wxT("Trying to connect to ") + Uint32_16toStringIP_Port(GetConnectIP(),GetUserPort()));
1590 m_socket->Connect(tmp, false);
1591 // We should send hello packets AFTER connecting!
1592 // so I moved it to OnConnect
1593 return true;
1594 } else {
1595 return false;
1599 void CUpDownClient::ConnectionEstablished()
1602 /* Kry - First thing, check if this client was just used to retrieve
1603 info. That's some debug thing for myself... check connection_reason
1604 definition */
1606 m_hasbeenobfuscatinglately = (m_socket && m_socket->IsConnected() && m_socket->IsObfusicating());
1608 #ifdef __DEBUG__
1609 if (!connection_reason.IsEmpty()) {
1610 AddLogLineN(CFormat(wxT("Got client info checking for %s: %s\nDisconnecting and deleting.")) % connection_reason % GetClientFullInfo());
1611 connection_reason.Clear(); // So we don't re-print on destructor.
1612 Safe_Delete();
1613 return;
1615 #endif
1617 // Check if we should use this client to retrieve our public IP
1618 // Ignore local ip on GetPublicIP (could be wrong)
1619 if (theApp->GetPublicIP(true) == 0 && theApp->IsConnectedED2K()) {
1620 SendPublicIPRequest();
1623 // was this a direct callback?
1624 if (m_dwDirectCallbackTimeout != 0){
1625 theApp->clientlist->RemoveDirectCallback(this);
1626 m_dwDirectCallbackTimeout = 0;
1627 // TODO LOGREMOVE
1628 AddDebugLogLineM(false, logClient, wxT("Direct Callback succeeded, connection established to ") + GetUserHash().Encode());
1631 switch (GetKadState()) {
1632 case KS_CONNECTING_FWCHECK:
1633 SetKadState(KS_CONNECTED_FWCHECK);
1634 break;
1635 case KS_CONNECTING_BUDDY:
1636 case KS_INCOMING_BUDDY:
1637 SetKadState(KS_CONNECTED_BUDDY);
1638 break;
1639 case KS_CONNECTING_FWCHECK_UDP:
1640 SetKadState(KS_FWCHECK_UDP);
1641 SendFirewallCheckUDPRequest();
1642 break;
1643 default:
1644 break;
1647 // ok we have a connection, lets see if we want anything from this client
1648 if (GetChatState() == MS_CONNECTING) {
1649 SetChatState( MS_CHATTING );
1652 if (GetChatState() == MS_CHATTING) {
1653 bool result = true;
1654 if (!m_pendingMessage.IsEmpty()) {
1655 result = SendChatMessage(m_pendingMessage);
1657 Notify_ChatConnResult(result,GUI_ID(GetIP(),GetUserPort()),m_pendingMessage);
1658 m_pendingMessage.Clear();
1661 switch(GetDownloadState()) {
1662 case DS_CONNECTING:
1663 case DS_WAITCALLBACK:
1664 case DS_WAITCALLBACKKAD:
1665 m_bReaskPending = false;
1666 SetDownloadState(DS_CONNECTED);
1667 SendFileRequest();
1669 if (m_bReaskPending){
1670 m_bReaskPending = false;
1671 if (GetDownloadState() != DS_NONE && GetDownloadState() != DS_DOWNLOADING) {
1672 SetDownloadState(DS_CONNECTED);
1673 SendFileRequest();
1676 switch(GetUploadState()){
1677 case US_CONNECTING:
1678 case US_WAITCALLBACK:
1679 if (theApp->uploadqueue->IsDownloading(this)) {
1680 SetUploadState(US_UPLOADING);
1681 CPacket* packet = new CPacket(OP_ACCEPTUPLOADREQ, 0, OP_EDONKEYPROT);
1682 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
1683 SendPacket(packet,true);
1684 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_ACCEPTUPLOADREQ to ") + GetFullIP() );
1687 if (m_iFileListRequested == 1) {
1688 CPacket* packet = new CPacket(m_fSharedDirectories ? OP_ASKSHAREDDIRS : OP_ASKSHAREDFILES, 0, OP_EDONKEYPROT);
1689 theStats::AddUpOverheadOther(packet->GetPacketSize());
1690 SendPacket(packet,true,true);
1691 if (m_fSharedDirectories) {
1692 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_ASKSHAREDDIRS to ") + GetFullIP() );
1693 } else {
1694 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_ASKSHAREDFILES to ") + GetFullIP() );
1698 while (!m_WaitingPackets_list.empty()) {
1699 CPacket* packet = m_WaitingPackets_list.front();
1700 m_WaitingPackets_list.pop_front();
1702 SendPacket(packet);
1707 int CUpDownClient::GetHashType() const
1709 if ( m_UserHash[5] == 13 && m_UserHash[14] == 110 ) {
1710 return SO_OLDEMULE;
1713 if ( m_UserHash[5] == 14 && m_UserHash[14] == 111 ) {
1714 return SO_EMULE;
1717 if ( m_UserHash[5] == 'M' && m_UserHash[14] == 'L' ) {
1718 return SO_MLDONKEY;
1721 return SO_UNKNOWN;
1725 void CUpDownClient::SetSocket(CClientTCPSocket* socket)
1727 #if defined(__DEBUG__) && !defined(EC_REMOTE)
1728 if (m_socket == NULL && socket != NULL) {
1729 theStats::SocketAssignedToClient();
1730 } else if (m_socket != NULL && socket == NULL) {
1731 theStats::SocketUnassignedFromClient();
1733 #endif
1734 m_socket = socket;
1738 void CUpDownClient::ReGetClientSoft()
1740 if (m_Username.IsEmpty()) {
1741 m_clientSoft=SO_UNKNOWN;
1742 m_clientVerString = m_clientSoftString = m_clientVersionString = m_fullClientVerString = _("Unknown");
1743 UpdateStats();
1744 return;
1747 int iHashType = GetHashType();
1748 wxString clientModString;
1749 if (iHashType == SO_EMULE) {
1751 m_clientSoft = m_byCompatibleClient;
1752 m_clientSoftString = GetSoftName(m_clientSoft);
1753 // Special issues:
1754 if(!GetClientModString().IsEmpty() && (m_clientSoft != SO_EMULE)) {
1755 m_clientSoftString = GetClientModString();
1757 // Isn't xMule annoying?
1758 if ((m_clientSoft == SO_LXMULE) && (GetMuleVersion() > 0x26) && (GetMuleVersion() != 0x99)) {
1759 m_clientSoftString += wxString::Format(_(" (Fake eMule version %#x)"),GetMuleVersion());
1761 if ((m_clientSoft == SO_EMULE) &&
1763 wxString(GetClientModString()).MakeLower().Find(wxT("xmule")) != -1
1764 || GetUserName().Find(wxT("xmule.")) != -1
1767 // FAKE eMule -a newer xMule faking is ident.
1768 m_clientSoft = SO_LXMULE;
1769 if (GetClientModString().IsEmpty() == false) {
1770 m_clientSoftString = GetClientModString() + _(" (Fake eMule)");
1771 } else {
1772 m_clientSoftString = _("xMule (Fake eMule)"); // don't use GetSoftName, it's not lmule.
1775 // Now, what if we don't know this SO_ID?
1776 if (m_clientSoftString.IsEmpty()) {
1777 if(m_bIsML) {
1778 m_clientSoft = SO_MLDONKEY;
1779 m_clientSoftString = GetSoftName(m_clientSoft);
1780 } else if (m_bIsHybrid) {
1781 m_clientSoft = SO_EDONKEYHYBRID;
1782 m_clientSoftString = GetSoftName(m_clientSoft);
1783 } else if (m_byCompatibleClient != 0) {
1784 m_clientSoft = SO_COMPAT_UNK;
1785 #ifdef __DEBUG__
1786 if (
1787 // Exceptions:
1788 (m_byCompatibleClient != 0xf0) // Chinese leech mod
1789 && (1==1) // Your ad here
1791 AddLogLineNS(CFormat(wxT("Compatible client found with ET_COMPATIBLECLIENT of %x")) % m_byCompatibleClient);
1793 #endif
1794 m_clientSoftString = GetSoftName(m_clientSoft) + wxString::Format(wxT("(%#x)"),m_byCompatibleClient);
1795 } else {
1796 // If we step here, it might mean 2 things:
1797 // a eMule
1798 // a Compat Client that has sent no MuleInfo packet yet.
1799 m_clientSoft = SO_EMULE;
1800 m_clientSoftString = wxT("eMule");
1804 if (m_byEmuleVersion == 0) {
1805 m_nClientVersion = MAKE_CLIENT_VERSION(0,0,0);
1806 } else if (m_byEmuleVersion != 0x99) {
1807 uint32 nClientMinVersion = (m_byEmuleVersion >> 4)*10 + (m_byEmuleVersion & 0x0f);
1808 m_nClientVersion = MAKE_CLIENT_VERSION(0,nClientMinVersion,0);
1809 switch (m_clientSoft) {
1810 case SO_AMULE:
1811 m_clientVerString = wxString::Format(_("1.x (based on eMule v0.%u)"), nClientMinVersion);
1812 break;
1813 case SO_LPHANT:
1814 m_clientVerString = wxT("< v0.05");
1815 break;
1816 default:
1817 clientModString = GetClientModString();
1818 m_clientVerString = wxString::Format(wxT("v0.%u"), nClientMinVersion);
1819 break;
1821 } else {
1822 uint32 nClientMajVersion = (m_nClientVersion >> 17) & 0x7f;
1823 uint32 nClientMinVersion = (m_nClientVersion >> 10) & 0x7f;
1824 uint32 nClientUpVersion = (m_nClientVersion >> 7) & 0x07;
1826 m_nClientVersion = MAKE_CLIENT_VERSION(nClientMajVersion, nClientMinVersion, nClientUpVersion);
1828 switch (m_clientSoft) {
1829 case SO_AMULE:
1830 case SO_LXMULE:
1831 case SO_HYDRANODE:
1832 // Kry - xMule started sending correct version tags on 1.9.1b.
1833 // It only took them 4 months, and being told by me and the
1834 // eMule+ developers, so I think they're slowly getting smarter.
1835 // They are based on our implementation, so we use the same format
1836 // for the version string.
1837 m_clientVerString = wxString::Format(wxT("v%u.%u.%u"), nClientMajVersion, nClientMinVersion, nClientUpVersion);
1838 break;
1839 case SO_LPHANT:
1840 m_clientVerString = wxString::Format(wxT(" v%u.%.2u%c"), nClientMajVersion-1, nClientMinVersion, 'a' + nClientUpVersion);
1841 break;
1842 case SO_EMULEPLUS:
1843 m_clientVerString = wxString::Format(wxT("v%u"), nClientMajVersion);
1844 if(nClientMinVersion != 0) {
1845 m_clientVerString += wxString::Format(wxT(".%u"), nClientMinVersion);
1847 if(nClientUpVersion != 0) {
1848 m_clientVerString += wxString::Format(wxT("%c"), 'a' + nClientUpVersion - 1);
1850 break;
1851 default:
1852 clientModString = GetClientModString();
1853 m_clientVerString = wxString::Format(wxT("v%u.%u%c"), nClientMajVersion, nClientMinVersion, 'a' + nClientUpVersion);
1854 break;
1857 } else if (m_bIsHybrid) {
1858 // seen:
1859 // 105010 50.10
1860 // 10501 50.1
1861 // 1051 51.0
1862 // 501 50.1
1864 m_clientSoft = SO_EDONKEYHYBRID;
1865 m_clientSoftString = GetSoftName(m_clientSoft);
1867 uint32 nClientMajVersion;
1868 uint32 nClientMinVersion;
1869 uint32 nClientUpVersion;
1870 if (m_nClientVersion > 100000) {
1871 uint32 uMaj = m_nClientVersion/100000;
1872 nClientMajVersion = uMaj - 1;
1873 nClientMinVersion = (m_nClientVersion - uMaj*100000) / 100;
1874 nClientUpVersion = m_nClientVersion % 100;
1876 else if (m_nClientVersion > 10000) {
1877 uint32 uMaj = m_nClientVersion/10000;
1878 nClientMajVersion = uMaj - 1;
1879 nClientMinVersion = (m_nClientVersion - uMaj*10000) / 10;
1880 nClientUpVersion = m_nClientVersion % 10;
1882 else if (m_nClientVersion > 1000) {
1883 uint32 uMaj = m_nClientVersion/1000;
1884 nClientMajVersion = uMaj - 1;
1885 nClientMinVersion = m_nClientVersion - uMaj*1000;
1886 nClientUpVersion = 0;
1888 else if (m_nClientVersion > 100) {
1889 uint32 uMin = m_nClientVersion/10;
1890 nClientMajVersion = 0;
1891 nClientMinVersion = uMin;
1892 nClientUpVersion = m_nClientVersion - uMin*10;
1894 else{
1895 nClientMajVersion = 0;
1896 nClientMinVersion = m_nClientVersion;
1897 nClientUpVersion = 0;
1899 m_nClientVersion = MAKE_CLIENT_VERSION(nClientMajVersion, nClientMinVersion, nClientUpVersion);
1900 if (nClientUpVersion) {
1901 m_clientVerString = wxString::Format(wxT("v%u.%u.%u"), nClientMajVersion, nClientMinVersion, nClientUpVersion);
1902 } else {
1903 m_clientVerString = wxString::Format(wxT("v%u.%u"), nClientMajVersion, nClientMinVersion);
1905 } else if (m_bIsML || (iHashType == SO_MLDONKEY)) {
1906 m_clientSoft = SO_MLDONKEY;
1907 m_clientSoftString = GetSoftName(m_clientSoft);
1908 uint32 nClientMinVersion = m_nClientVersion;
1909 m_nClientVersion = MAKE_CLIENT_VERSION(0, nClientMinVersion, 0);
1910 m_clientVerString = wxString::Format(wxT("v0.%u"), nClientMinVersion);
1911 } else if (iHashType == SO_OLDEMULE) {
1912 m_clientSoft = SO_OLDEMULE;
1913 m_clientSoftString = GetSoftName(m_clientSoft);
1914 uint32 nClientMinVersion = m_nClientVersion;
1915 m_nClientVersion = MAKE_CLIENT_VERSION(0, nClientMinVersion, 0);
1916 m_clientVerString = wxString::Format(wxT("v0.%u"), nClientMinVersion);
1917 } else {
1918 m_clientSoft = SO_EDONKEY;
1919 m_clientSoftString = GetSoftName(m_clientSoft);
1920 m_nClientVersion *= 10;
1921 m_clientVerString = wxString::Format(wxT("v%u.%u"), m_nClientVersion / 100000, (m_nClientVersion / 1000) % 100);
1924 m_clientVersionString = m_clientVerString;
1925 if (!clientModString.IsEmpty()) {
1926 m_clientVerString += wxT(" - ") + clientModString;
1928 m_fullClientVerString = m_clientSoftString + wxT(" ") + m_clientVerString;
1930 UpdateStats();
1933 void CUpDownClient::RequestSharedFileList()
1935 if (m_iFileListRequested == 0) {
1936 AddDebugLogLineM( false, logClient, wxString( wxT("Requesting shared files from ") ) + GetUserName() );
1937 m_iFileListRequested = 1;
1938 TryToConnect(true);
1939 } else {
1940 AddDebugLogLineM( false, logClient, CFormat( wxT("Requesting shared files from user %s (%u) is already in progress") ) % GetUserName() % GetUserIDHybrid() );
1945 void CUpDownClient::ProcessSharedFileList(const byte* pachPacket, uint32 nSize, wxString& pszDirectory)
1947 if (m_iFileListRequested > 0) {
1948 m_iFileListRequested--;
1949 theApp->searchlist->ProcessSharedFileList(pachPacket, nSize, this, NULL, pszDirectory);
1954 void CUpDownClient::ResetFileStatusInfo()
1956 m_nPartCount = 0;
1958 if ( m_reqfile ) {
1959 m_reqfile->UpdatePartsFrequency( this, false );
1961 m_downPartStatus.clear();
1963 m_clientFilename.Clear();
1965 m_bCompleteSource = false;
1966 m_dwLastAskedTime = 0;
1967 m_iRating = 0;
1968 m_strComment.Clear();
1970 if (m_pReqFileAICHHash != NULL) {
1971 delete m_pReqFileAICHHash;
1972 m_pReqFileAICHHash = NULL;
1977 wxString CUpDownClient::GetUploadFileInfo()
1979 // build info text and display it
1980 wxString sRet;
1981 sRet = (CFormat(_("NickName: %s ID: %u")) % GetUserName() % GetUserIDHybrid()) + wxT(" ");
1982 if (m_reqfile) {
1983 sRet += CFormat(_("Requested: %s\n")) % m_reqfile->GetFileName();
1984 sRet += CFormat(
1985 wxPLURAL("Filestats for this session: Accepted %d of %d request, %s transferred\n", "Filestats for this session: Accepted %d of %d requests, %s transferred\n", m_reqfile->statistic.GetRequests())
1986 ) % m_reqfile->statistic.GetAccepts() % m_reqfile->statistic.GetRequests() % CastItoXBytes(m_reqfile->statistic.GetTransferred());
1987 sRet += CFormat(
1988 wxPLURAL("Filestats for all sessions: Accepted %d of %d request, %s transferred\n", "Filestats for all sessions: Accepted %d of %d requests, %s transferred\n", m_reqfile->statistic.GetAllTimeRequests())
1989 ) % m_reqfile->statistic.GetAllTimeAccepts() % m_reqfile->statistic.GetAllTimeRequests() % CastItoXBytes(m_reqfile->statistic.GetAllTimeTransferred());
1990 } else {
1991 sRet += _("Requested unknown file");
1993 return sRet;
1996 // sends a packet, if needed it will establish a connection before
1997 // options used: ignore max connections, control packet, delete packet
1998 // !if the functions returns false it is _possible_ that this clientobject was deleted, because the connectiontry fails
1999 bool CUpDownClient::SafeSendPacket(CPacket* packet)
2001 if (IsConnected()) {
2002 SendPacket(packet, true);
2003 return true;
2004 } else {
2005 m_WaitingPackets_list.push_back(packet);
2006 return TryToConnect(true);
2010 void CUpDownClient::SendPublicKeyPacket(){
2011 // send our public key to the client who requested it
2012 if (m_socket == NULL || credits == NULL || m_SecureIdentState != IS_KEYANDSIGNEEDED){
2013 wxFAIL;
2014 return;
2016 if (!theApp->CryptoAvailable())
2017 return;
2019 CMemFile data;
2020 data.WriteUInt8(theApp->clientcredits->GetPubKeyLen());
2021 data.Write(theApp->clientcredits->GetPublicKey(), theApp->clientcredits->GetPubKeyLen());
2022 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_PUBLICKEY);
2024 theStats::AddUpOverheadOther(packet->GetPacketSize());
2025 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_PUBLICKEY to ") + GetFullIP() );
2026 SendPacket(packet,true,true);
2027 m_SecureIdentState = IS_SIGNATURENEEDED;
2031 void CUpDownClient::SendSignaturePacket(){
2032 // signate the public key of this client and send it
2033 if (m_socket == NULL || credits == NULL || m_SecureIdentState == 0){
2034 wxFAIL;
2035 return;
2038 if (!theApp->CryptoAvailable()) {
2039 return;
2041 if (credits->GetSecIDKeyLen() == 0) {
2042 return; // We don't have his public key yet, will be back here later
2044 // do we have a challenge value received (actually we should if we are in this function)
2045 if (credits->m_dwCryptRndChallengeFrom == 0){
2046 AddDebugLogLineM( false, logClient, wxString(wxT("Want to send signature but challenge value is invalid - User ")) + GetUserName());
2047 return;
2049 // v2
2050 // we will use v1 as default, except if only v2 is supported
2051 bool bUseV2;
2052 if ( (m_bySupportSecIdent&1) == 1 )
2053 bUseV2 = false;
2054 else
2055 bUseV2 = true;
2057 uint8 byChaIPKind = 0;
2058 uint32 ChallengeIP = 0;
2059 if (bUseV2){
2060 if (::IsLowID(theApp->GetED2KID())) {
2061 // we cannot do not know for sure our public ip, so use the remote clients one
2062 ChallengeIP = GetIP();
2063 byChaIPKind = CRYPT_CIP_REMOTECLIENT;
2064 } else {
2065 ChallengeIP = theApp->GetED2KID();
2066 byChaIPKind = CRYPT_CIP_LOCALCLIENT;
2069 //end v2
2070 byte achBuffer[250];
2072 uint8 siglen = theApp->clientcredits->CreateSignature(credits, achBuffer, 250, ChallengeIP, byChaIPKind );
2073 if (siglen == 0){
2074 wxFAIL;
2075 return;
2077 CMemFile data;
2078 data.WriteUInt8(siglen);
2079 data.Write(achBuffer, siglen);
2080 if (bUseV2) {
2081 data.WriteUInt8(byChaIPKind);
2084 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_SIGNATURE);
2086 theStats::AddUpOverheadOther(packet->GetPacketSize());
2087 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_SIGNATURE to ") + GetFullIP() );
2088 SendPacket(packet,true,true);
2089 m_SecureIdentState = IS_ALLREQUESTSSEND;
2093 void CUpDownClient::ProcessPublicKeyPacket(const byte* pachPacket, uint32 nSize)
2095 theApp->clientlist->AddTrackClient(this);
2097 if (m_socket == NULL || credits == NULL || pachPacket[0] != nSize-1
2098 || nSize == 0 || nSize > 250){
2099 wxFAIL;
2100 return;
2102 if (!theApp->CryptoAvailable())
2103 return;
2104 // the function will handle everything (mulitple key etc)
2105 if (credits->SetSecureIdent(pachPacket+1, pachPacket[0])){
2106 // if this client wants a signature, now we can send him one
2107 if (m_SecureIdentState == IS_SIGNATURENEEDED){
2108 SendSignaturePacket();
2110 else if(m_SecureIdentState == IS_KEYANDSIGNEEDED){
2111 // something is wrong
2112 AddDebugLogLineM( false, logClient, wxT("Invalid State error: IS_KEYANDSIGNEEDED in ProcessPublicKeyPacket") );
2114 } else{
2115 AddDebugLogLineM( false, logClient, wxT("Failed to use new received public key") );
2120 void CUpDownClient::ProcessSignaturePacket(const byte* pachPacket, uint32 nSize)
2122 // here we spread the good guys from the bad ones ;)
2124 if (m_socket == NULL || credits == NULL || nSize == 0 || nSize > 250){
2125 wxFAIL;
2126 return;
2129 uint8 byChaIPKind;
2130 if (pachPacket[0] == nSize-1)
2131 byChaIPKind = 0;
2132 else if (pachPacket[0] == nSize-2 && (m_bySupportSecIdent & 2) > 0) //v2
2133 byChaIPKind = pachPacket[nSize-1];
2134 else{
2135 wxFAIL;
2136 return;
2139 if (!theApp->CryptoAvailable())
2140 return;
2142 // we accept only one signature per IP, to avoid floods which need a lot cpu time for cryptfunctions
2143 if (m_dwLastSignatureIP == GetIP()){
2144 AddDebugLogLineM( false, logClient, wxT("received multiple signatures from one client") );
2145 return;
2147 // also make sure this client has a public key
2148 if (credits->GetSecIDKeyLen() == 0){
2149 AddDebugLogLineM( false, logClient, wxT("received signature for client without public key") );
2150 return;
2152 // and one more check: did we ask for a signature and sent a challange packet?
2153 if (credits->m_dwCryptRndChallengeFor == 0){
2154 AddDebugLogLineM( false, logClient, wxT("received signature for client with invalid challenge value - User ") + GetUserName() );
2155 return;
2158 if (theApp->clientcredits->VerifyIdent(credits, pachPacket+1, pachPacket[0], GetIP(), byChaIPKind ) ){
2159 // result is saved in function above
2160 AddDebugLogLineM( false, logClient, CFormat( wxT("'%s' has passed the secure identification, V2 State: %i") ) % GetUserName() % byChaIPKind );
2161 } else {
2162 AddDebugLogLineM( false, logClient, CFormat( wxT("'%s' has failed the secure identification, V2 State: %i") ) % GetUserName() % byChaIPKind );
2165 m_dwLastSignatureIP = GetIP();
2168 void CUpDownClient::SendSecIdentStatePacket(){
2169 // check if we need public key and signature
2170 uint8 nValue = 0;
2171 if (credits){
2172 if (theApp->CryptoAvailable()){
2173 if (credits->GetSecIDKeyLen() == 0) {
2174 nValue = IS_KEYANDSIGNEEDED;
2175 } else if (m_dwLastSignatureIP != GetIP()) {
2176 nValue = IS_SIGNATURENEEDED;
2179 if (nValue == 0){
2180 AddDebugLogLineM( false, logClient, wxT("Not sending SecIdentState Packet, because State is Zero") );
2181 return;
2183 // crypt: send random data to sign
2184 uint32 dwRandom = rand()+1;
2185 credits->m_dwCryptRndChallengeFor = dwRandom;
2187 CMemFile data;
2188 data.WriteUInt8(nValue);
2189 data.WriteUInt32(dwRandom);
2190 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_SECIDENTSTATE);
2192 theStats::AddUpOverheadOther(packet->GetPacketSize());
2193 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_SECIDENTSTATE to ") + GetFullIP() );
2194 SendPacket(packet,true,true);
2195 } else {
2196 wxFAIL;
2201 void CUpDownClient::ProcessSecIdentStatePacket(const byte* pachPacket, uint32 nSize)
2203 if ( nSize != 5 ) {
2204 return;
2207 if ( !credits ) {
2208 wxASSERT( credits );
2209 return;
2212 CMemFile data(pachPacket,nSize);
2214 switch ( data.ReadUInt8() ) {
2215 case 0:
2216 m_SecureIdentState = IS_UNAVAILABLE;
2217 break;
2218 case 1:
2219 m_SecureIdentState = IS_SIGNATURENEEDED;
2220 break;
2221 case 2:
2222 m_SecureIdentState = IS_KEYANDSIGNEEDED;
2223 break;
2224 default:
2225 return;
2228 credits->m_dwCryptRndChallengeFrom = data.ReadUInt32();
2232 void CUpDownClient::InfoPacketsReceived()
2234 // indicates that both Information Packets has been received
2235 // needed for actions, which process data from both packets
2236 wxASSERT ( m_byInfopacketsReceived == IP_BOTH );
2237 m_byInfopacketsReceived = IP_NONE;
2239 if (m_bySupportSecIdent){
2240 SendSecIdentStatePacket();
2245 bool CUpDownClient::CheckHandshakeFinished(uint32 WXUNUSED(protocol), uint32 WXUNUSED(opcode)) const
2247 if (m_bHelloAnswerPending) {
2248 // this triggers way too often.. need more time to look at this -> only create a warning
2249 // The reason for this is that 2 clients are connecting to each other at the same time..
2250 AddDebugLogLineM( false, logClient, wxT("Handshake not finished while processing packet.") );
2251 return false;
2254 return true;
2258 wxString CUpDownClient::GetClientFullInfo()
2261 if (m_clientVerString.IsEmpty()) {
2262 ReGetClientSoft();
2265 return CFormat( _("Client %s on IP:Port %s:%d using %s %s %s") )
2266 % ( m_Username.IsEmpty() ? wxString(_("Unknown")) : m_Username )
2267 % GetFullIP()
2268 % GetUserPort()
2269 % m_clientSoftString
2270 % m_clientVerString
2271 % m_strModVersion;
2276 void CUpDownClient::SendPublicIPRequest(){
2277 if (IsConnected()){
2278 CPacket* packet = new CPacket(OP_PUBLICIP_REQ,0,OP_EMULEPROT);
2279 theStats::AddUpOverheadOther(packet->GetPacketSize());
2280 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_PUBLICIP_REQ to") + GetFullIP());
2281 SendPacket(packet,true);
2282 m_fNeedOurPublicIP = true;
2286 void CUpDownClient::ProcessPublicIPAnswer(const byte* pbyData, uint32 uSize){
2287 if (uSize != 4) {
2288 throw wxString(wxT("Wrong Packet size on Public IP answer"));
2290 uint32 dwIP = PeekUInt32(pbyData);
2291 if (m_fNeedOurPublicIP == true){ // did we?
2292 m_fNeedOurPublicIP = false;
2293 // Ignore local ip on GetPublicIP (could be wrong)
2294 if (theApp->GetPublicIP(true) == 0 && !IsLowID(dwIP) ) {
2295 theApp->SetPublicIP(dwIP);
2301 bool CUpDownClient::IsConnected() const
2303 return m_socket && m_socket->IsConnected();
2306 bool CUpDownClient::SendPacket(CPacket* packet, bool delpacket, bool controlpacket)
2308 if ( m_socket ) {
2309 m_socket->SendPacket(packet, delpacket, controlpacket );
2310 return true;
2311 } else {
2312 AddLogLineN(wxT("CAUGHT DEAD SOCKET IN SENDPACKET()"));
2313 return false;
2317 float CUpDownClient::SetDownloadLimit(uint32 reducedownload)
2320 // lfroen: in daemon it actually can happen
2321 wxASSERT( m_socket );
2323 float kBpsClient = CalculateKBpsDown();
2325 if ( m_socket ) {
2327 if (reducedownload) {
2328 // (% to reduce * current speed) / 100 and THEN, / 10 because this
2329 // gets called 10 times per second.
2330 uint32 limit = (uint32)(((float)reducedownload*(kBpsClient*1024.0))/1000);
2332 if(limit<1024 && reducedownload >= 200) {
2333 // If we're going up and this download is < 1kB,
2334 // we want it to go up fast. Can be reduced later,
2335 // and it'll probably be in a more fair way with
2336 // other downloads that are faster.
2337 limit +=1024;
2338 } else if(limit == 0) {
2339 // This download is not transferring yet... make it
2340 // 1024 so we don't fill the TCP stack and lose the
2341 // connection.
2342 limit = 1024;
2345 m_socket->SetDownloadLimit(limit);
2346 } else {
2347 m_socket->DisableDownloadLimit();
2350 } else {
2351 AddLogLineNS(CFormat(wxT("CAUGHT DEAD SOCKET IN SETDOWNLOADLIMIT() WITH SPEED %f")) % kBpsClient);
2354 return kBpsClient;
2358 void CUpDownClient::SetUserIDHybrid(uint32 nUserID)
2360 theApp->clientlist->UpdateClientID( this, nUserID );
2362 m_nUserIDHybrid = nUserID;
2366 void CUpDownClient::SetIP( uint32 val )
2368 theApp->clientlist->UpdateClientIP( this, val );
2370 m_dwUserIP = val;
2372 m_nConnectIP = val;
2374 m_FullUserIP = val;
2378 void CUpDownClient::SetUserHash(const CMD4Hash& userhash)
2380 theApp->clientlist->UpdateClientHash( this, userhash );
2382 m_UserHash = userhash;
2384 ValidateHash();
2387 EUtf8Str CUpDownClient::GetUnicodeSupport() const
2389 return m_bUnicodeSupport ? utf8strRaw : utf8strNone;
2392 void CUpDownClient::SetSpammer(bool bVal)
2394 if (bVal) {
2395 Ban();
2396 } else if (IsBanned() && m_fIsSpammer) {
2397 UnBan();
2399 m_fIsSpammer = bVal;
2402 uint8 CUpDownClient::GetSecureIdentState()
2404 if (m_SecureIdentState != IS_UNAVAILABLE) {
2405 if (!SecIdentSupRec) {
2406 // This can be caused by a 0.30x based client which sends the old
2407 // style Hello packet, and the mule info packet, but between them they
2408 // send a secure ident state packet (after a hello but before we have
2409 // the SUI capabilities). This is a misbehaving client, and somehow I
2410 // feel like it should be dropped. But then again, it won't harm to use
2411 // this SUI state if they are reporting no SUI (won't be used) and if
2412 // they report using SUI on the mule info packet, it's ok to use it.
2414 AddDebugLogLineM(false, logClient, wxT("A client sent secure ident state before telling us the SUI capabilities"));
2415 AddDebugLogLineM(false, logClient, wxT("Client info: ") + GetClientFullInfo());
2416 AddDebugLogLineM(false, logClient, wxT("This client won't be disconnected, but it should be. :P"));
2420 return m_SecureIdentState;
2424 bool CUpDownClient::SendChatMessage(const wxString& message)
2426 if (GetChatCaptchaState() == CA_CAPTCHARECV) {
2427 m_nChatCaptchaState = CA_SOLUTIONSENT;
2428 } else if (GetChatCaptchaState() == CA_SOLUTIONSENT) {
2429 wxFAIL; // we responsed to a captcha but didn't heard from the client afterwards - hopefully its just lag and this message will get through
2430 } else {
2431 m_nChatCaptchaState = CA_ACCEPTING;
2434 SetSpammer(false);
2435 IncMessagesSent();
2436 // Already connecting?
2437 if (GetChatState() == MS_CONNECTING) {
2438 // Queue all messages till we're able to send them (or discard them)
2439 if (!m_pendingMessage.IsEmpty()) {
2440 m_pendingMessage += wxT("\n");
2441 } else {
2442 // There must be a message to send
2443 // - except if we got disconnected. No need to assert therefore.
2445 m_pendingMessage += message;
2446 return false;
2448 if (IsConnected()) {
2449 // If we are already connected when we send the first message,
2450 // we have to update the chat status.
2451 SetChatState(MS_CHATTING);
2452 CMemFile data;
2453 data.WriteString(message, GetUnicodeSupport());
2454 CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_MESSAGE);
2455 theStats::AddUpOverheadOther(packet->GetPacketSize());
2456 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_MESSAGE to ") + GetFullIP());
2457 SendPacket(packet, true, true);
2458 return true;
2459 } else {
2460 m_pendingMessage = message;
2461 SetChatState(MS_CONNECTING);
2462 // True to ignore "Too Many Connections"
2463 TryToConnect(true);
2464 return false;
2468 /* Kad stuff */
2470 void CUpDownClient::SetBuddyID(const byte* pucBuddyID)
2472 if( pucBuddyID == NULL ){
2473 md4clr(m_achBuddyID);
2474 m_bBuddyIDValid = false;
2475 return;
2477 m_bBuddyIDValid = true;
2478 md4cpy(m_achBuddyID, pucBuddyID);
2481 // Kad added by me
2483 bool CUpDownClient::SendBuddyPing() {
2484 SetLastBuddyPingPongTime();
2485 CPacket* buddyPing = new CPacket(OP_BUDDYPING, 0, OP_EMULEPROT);
2486 theStats::AddUpOverheadKad(buddyPing->GetPacketSize());
2487 AddDebugLogLineM(false, logLocalClient,wxT("Local Client: OP_BUDDYPING to ") + GetFullIP());
2488 return SafeSendPacket(buddyPing);
2492 /* Statistics */
2494 void CUpDownClient::UpdateStats()
2496 if (m_lastClientSoft != m_clientSoft || m_lastClientVersion != m_nClientVersion || m_lastOSInfo != m_sClientOSInfo) {
2497 if (m_lastClientSoft == SO_UNKNOWN) {
2498 theStats::RemoveUnknownClient();
2499 } else if (m_lastClientSoft != (uint32)(-1)) {
2500 theStats::RemoveKnownClient(m_lastClientSoft, m_lastClientVersion, m_lastOSInfo);
2503 m_lastClientSoft = m_clientSoft;
2504 m_lastClientVersion = m_nClientVersion;
2505 m_lastOSInfo = m_sClientOSInfo;
2507 if (m_clientSoft == SO_UNKNOWN) {
2508 theStats::AddUnknownClient();
2509 } else {
2510 theStats::AddKnownClient(this);
2515 bool CUpDownClient::IsIdentified() const
2517 return (credits && credits->GetCurrentIdentState(GetIP()) == IS_IDENTIFIED);
2520 bool CUpDownClient::IsBadGuy() const
2522 return (credits && credits->GetCurrentIdentState(GetIP()) == IS_IDBADGUY);
2525 bool CUpDownClient::SUIFailed() const
2527 return (credits && credits->GetCurrentIdentState(GetIP()) == IS_IDFAILED);
2530 bool CUpDownClient::SUINeeded() const
2532 return (credits && credits->GetCurrentIdentState(GetIP()) == IS_IDNEEDED);
2535 bool CUpDownClient::SUINotSupported() const
2537 return (credits && credits->GetCurrentIdentState(GetIP()) == IS_NOTAVAILABLE);
2540 uint64 CUpDownClient::GetDownloadedTotal() const
2542 return credits ? credits->GetDownloadedTotal() : 0;
2545 uint64 CUpDownClient::GetUploadedTotal() const
2547 return credits ? credits->GetUploadedTotal() : 0;
2550 double CUpDownClient::GetScoreRatio() const
2552 return credits ? credits->GetScoreRatio(GetIP(), theApp->CryptoAvailable()) : 0;
2555 const wxString CUpDownClient::GetServerName() const
2557 wxString ret;
2558 wxString srvaddr = Uint32toStringIP(GetServerIP());
2559 CServer* cserver = theApp->serverlist->GetServerByAddress(
2560 srvaddr, GetServerPort());
2561 if (cserver) {
2562 ret = cserver->GetListName();
2563 } else {
2564 ret = _("Unknown");
2567 return ret;
2570 bool CUpDownClient::ShouldReceiveCryptUDPPackets() const
2572 return (thePrefs::IsClientCryptLayerSupported() && SupportsCryptLayer() && theApp->GetPublicIP() != 0
2573 && HasValidHash() && (thePrefs::IsClientCryptLayerRequested() || RequestsCryptLayer()) );
2576 #ifdef AMULE_DAEMON
2578 void CUpDownClient::ProcessCaptchaRequest(CMemFile* WXUNUSED(data)) {}
2579 void CUpDownClient::ProcessCaptchaReqRes(uint8 WXUNUSED(nStatus)) {}
2580 void CUpDownClient::ProcessChatMessage(const wxString WXUNUSED(message)) {}
2582 #else
2584 void CUpDownClient::ProcessCaptchaRequest(CMemFile* data)
2586 uint64 id = GUI_ID(GetIP(),GetUserPort());
2587 // received a captcha request, check if we actually accept it (only after sending a message ourself to this client)
2588 if (GetChatCaptchaState() == CA_ACCEPTING && GetChatState() != MS_NONE
2589 && theApp->amuledlg->m_chatwnd->IsIdValid(id)) {
2590 // read tags (for future use)
2591 uint8 nTagCount = data->ReadUInt8();
2592 if (nTagCount) {
2593 AddDebugLogLineN(logClient, CFormat(wxT("Received captcha request from client (%s) with (%u) tags")) % GetFullIP() % nTagCount);
2594 // and ignore them for now
2595 for (uint32 i = 0; i < nTagCount; i++) {
2596 CTag tag(*data, true);
2600 // sanitize checks - we want a small captcha not a wallpaper
2601 uint32 nSize = (uint32)(data->GetLength() - data->GetPosition());
2603 if ( nSize > 128 && nSize < 4096 ) {
2604 uint64 pos = data->GetPosition();
2605 wxMemoryInputStream memstr(data->GetRawBuffer() + pos, nSize);
2606 wxImage imgCaptcha(memstr, wxBITMAP_TYPE_BMP);
2608 if (imgCaptcha.IsOk() && imgCaptcha.GetHeight() > 10 && imgCaptcha.GetHeight() < 50
2609 && imgCaptcha.GetWidth() > 10 && imgCaptcha.GetWidth() < 150 ) {
2610 m_nChatCaptchaState = CA_CAPTCHARECV;
2611 CCaptchaDialog * dialog = new CCaptchaDialog(theApp->amuledlg, imgCaptcha, id);
2612 dialog->Show();
2614 } else {
2615 AddDebugLogLineN(logClient, CFormat(wxT("Received captcha request from client, processing image failed or invalid pixel size (%s)")) % GetFullIP());
2617 } else {
2618 AddDebugLogLineN(logClient, CFormat(wxT("Received captcha request from client, size sanitize check failed (%u) (%s)")) % nSize % GetFullIP());
2620 } else {
2621 AddDebugLogLineN(logClient, CFormat(wxT("Received captcha request from client, but don't accepting it at this time (%s)")) % GetFullIP());
2625 void CUpDownClient::ProcessCaptchaReqRes(uint8 nStatus)
2627 uint64 id = GUI_ID(GetIP(),GetUserPort());
2628 if (GetChatCaptchaState() == CA_SOLUTIONSENT && GetChatState() != MS_NONE
2629 && theApp->amuledlg->m_chatwnd->IsIdValid(id)) {
2630 wxASSERT( nStatus < 3 );
2631 m_nChatCaptchaState = CA_NONE;
2632 theApp->amuledlg->m_chatwnd->ShowCaptchaResult(id, nStatus == 0);
2633 } else {
2634 m_nChatCaptchaState = CA_NONE;
2635 AddDebugLogLineN(logClient, CFormat(wxT("Received captcha result from client, but not accepting it at this time (%s)")) % GetFullIP());
2639 void CUpDownClient::ProcessChatMessage(wxString message)
2641 if (IsMessageFiltered(message)) {
2642 AddLogLineM( true, CFormat(_("Message filtered from '%s' (IP:%s)")) % GetUserName() % GetFullIP());
2643 return;
2646 // advanced spamfilter check
2647 if (thePrefs::IsChatCaptchaEnabled() && !IsFriend()) {
2648 // captcha checks outrank any further checks - if the captcha has been solved, we assume it's not spam
2649 // first check if we need to send a captcha request to this client
2650 if (GetMessagesSent() == 0 && GetMessagesReceived() == 0 && GetChatCaptchaState() != CA_CAPTCHASOLVED) {
2651 // we have never sent a message to this client, and no message from him has ever passed our filters
2652 if (GetChatCaptchaState() != CA_CHALLENGESENT) {
2653 // we also aren't currently expecting a captcha response
2654 if (m_fSupportsCaptcha) {
2655 // and he supports captcha, so send him one and store the message (without showing for now)
2656 if (m_cCaptchasSent < 3) { // no more than 3 tries
2657 m_strCaptchaPendingMsg = message;
2658 wxMemoryOutputStream memstr;
2659 memstr.PutC(0); // no tags, for future use
2660 CCaptchaGenerator captcha(4);
2661 if (captcha.WriteCaptchaImage(memstr)){
2662 m_strCaptchaChallenge = captcha.GetCaptchaText();
2663 m_nChatCaptchaState = CA_CHALLENGESENT;
2664 m_cCaptchasSent++;
2665 CMemFile fileAnswer((byte*) memstr.GetOutputStreamBuffer()->GetBufferStart(), memstr.GetLength());
2666 CPacket* packet = new CPacket(fileAnswer, OP_EMULEPROT, OP_CHATCAPTCHAREQ);
2667 theStats::AddUpOverheadOther(packet->GetPacketSize());
2668 AddLogLineN(CFormat(wxT("sent Captcha %s (%d)")) % m_strCaptchaChallenge % packet->GetPacketSize());
2669 SafeSendPacket(packet);
2670 } else {
2671 wxFAIL;
2674 } else {
2675 // client doesn't support captchas, but we require them, tell him that it's not going to work out
2676 // with an answer message (will not be shown and doesn't count as sent message)
2677 if (m_cCaptchasSent < 1) { // don't send this notifier more than once
2678 m_cCaptchasSent++;
2679 // always sent in english
2680 SendChatMessage(wxT("In order to avoid spam messages, this user requires you to solve a captcha before you can send a message to him. However your client does not supports captchas, so you will not be able to chat with this user."));
2681 AddDebugLogLineN(logClient, CFormat(wxT("Received message from client not supporting captchas, filtered and sent notifier (%s)")) % GetClientFullInfo());
2682 } else {
2683 AddDebugLogLineN(logClient, CFormat(wxT("Received message from client not supporting captchas, filtered, didn't send notifier (%s)")) % GetClientFullInfo());
2686 return;
2687 } else { // (GetChatCaptchaState() == CA_CHALLENGESENT)
2688 // this message must be the answer to the captcha request we sent him, let's verify
2689 wxASSERT( !m_strCaptchaChallenge.IsEmpty() );
2690 if (m_strCaptchaChallenge.CmpNoCase(message.Trim().Right(std::min(message.Length(), m_strCaptchaChallenge.Length()))) == 0) {
2691 // allright
2692 AddDebugLogLineN(logClient, CFormat(wxT("Captcha solved, showing withheld message (%s)")) % GetClientFullInfo());
2693 m_nChatCaptchaState = CA_CAPTCHASOLVED; // this state isn't persitent, but the messagecounter will be used to determine later if the captcha has been solved
2694 // replace captchaanswer with withheld message and show it
2695 message = m_strCaptchaPendingMsg;
2696 m_cCaptchasSent = 0;
2697 m_strCaptchaChallenge = wxEmptyString;
2698 CPacket* packet = new CPacket(OP_CHATCAPTCHARES, 1, OP_EMULEPROT, false);
2699 byte statusResponse = 0; // status response
2700 packet->CopyToDataBuffer(0, &statusResponse, 1);
2701 theStats::AddUpOverheadOther(packet->GetPacketSize());
2702 SafeSendPacket(packet);
2703 } else { // wrong, cleanup and ignore
2704 AddDebugLogLineN(logClient, CFormat(wxT("Captcha answer failed (%s)")) % GetClientFullInfo());
2705 m_nChatCaptchaState = CA_NONE;
2706 m_strCaptchaChallenge = wxEmptyString;
2707 m_strCaptchaPendingMsg = wxEmptyString;
2708 CPacket* packet = new CPacket(OP_CHATCAPTCHARES, 1, OP_EMULEPROT, false);
2709 byte statusResponse = (m_cCaptchasSent < 3) ? 1 : 2; // status response
2710 packet->CopyToDataBuffer(0, &statusResponse, 1);
2711 theStats::AddUpOverheadOther(packet->GetPacketSize());
2712 SafeSendPacket(packet);
2713 return; // nothing more todo
2719 if (thePrefs::IsAdvancedSpamfilterEnabled() && !IsFriend()) { // friends are never spammer... (but what if two spammers are friends :P )
2720 bool bIsSpam = false;
2721 if (m_fIsSpammer) {
2722 bIsSpam = true;
2723 } else {
2724 // first fixed criteria: If a client sends me an URL in his first message before I respond to him
2725 // there is a 99,9% chance that it is some poor guy advising his leech mod, or selling you .. well you know :P
2726 if (GetMessagesSent() == 0) {
2727 static wxArrayString urlindicators(wxStringTokenize(wxT("http:|www.|.de |.net |.com |.org |.to |.tk |.cc |.fr |ftp:|ed2k:|https:|ftp.|.info|.biz|.uk|.eu|.es|.tv|.cn|.tw|.ws|.nu|.jp"), wxT("|")));
2728 for (size_t pos = urlindicators.GetCount(); pos--;) {
2729 if (message.Find(urlindicators[pos]) != wxNOT_FOUND) {
2730 bIsSpam = true;
2731 break;
2734 // second fixed criteria: he sent me 4 or more messages and I didn't answer him once
2735 if (GetMessagesReceived() > 3) {
2736 bIsSpam = true;
2740 if (bIsSpam) {
2741 AddDebugLogLineN(logClient, CFormat(wxT("'%s' has been marked as spammer")) % GetUserName());
2742 SetSpammer(true);
2743 theApp->amuledlg->m_chatwnd->EndSession(GUI_ID(GetIP(),GetUserPort()));
2744 return;
2749 wxString logMsg = CFormat(_("New message from '%s' (IP:%s)")) % GetUserName() % GetFullIP();
2750 if(thePrefs::ShowMessagesInLog()) {
2751 logMsg += wxT(": ") + message;
2753 AddLogLineM(true, logMsg);
2754 IncMessagesReceived();
2756 Notify_ChatProcessMsg(GUI_ID(GetIP(), GetUserPort()), GetUserName() + wxT("|") + message);
2759 bool CUpDownClient::IsMessageFiltered(const wxString& message)
2761 bool filtered = false;
2762 // If we're chatting to the guy, we don't want to filter!
2763 if (GetChatState() != MS_CHATTING) {
2764 if (thePrefs::MsgOnlyFriends() && !IsFriend()) {
2765 filtered = true;
2766 } else if (thePrefs::MsgOnlySecure() && GetUserName().IsEmpty() ) {
2767 filtered = true;
2768 } else if (thePrefs::MustFilterMessages()) {
2769 filtered = thePrefs::IsMessageFiltered(message);
2772 if (filtered) {
2773 SetSpammer(true);
2775 return filtered;
2778 #endif
2781 void CUpDownClient::SendFirewallCheckUDPRequest()
2783 wxASSERT(GetKadState() == KS_FWCHECK_UDP);
2785 if (!Kademlia::CKademlia::IsRunning()) {
2786 SetKadState(KS_NONE);
2787 return;
2788 } else if (GetUploadState() != US_NONE || GetDownloadState() != DS_NONE || GetChatState() != MS_NONE || GetKadVersion() <= 5 || GetKadPort() == 0) {
2789 Kademlia::CUDPFirewallTester::SetUDPFWCheckResult(false, true, wxUINT32_SWAP_ALWAYS(GetIP()), 0); // inform the tester that this test was cancelled
2790 SetKadState(KS_NONE);
2791 return;
2794 CMemFile data;
2795 data.WriteUInt16(Kademlia::CKademlia::GetPrefs()->GetInternKadPort());
2796 data.WriteUInt16(Kademlia::CKademlia::GetPrefs()->GetExternalKadPort());
2797 data.WriteUInt32(Kademlia::CKademlia::GetPrefs()->GetUDPVerifyKey(GetConnectIP()));
2798 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_FWCHECKUDPREQ);
2799 theStats::AddUpOverheadKad(packet->GetPacketSize());
2800 SafeSendPacket(packet);
2803 void CUpDownClient::ProcessFirewallCheckUDPRequest(CMemFile* data)
2805 if (!Kademlia::CKademlia::IsRunning() || Kademlia::CKademlia::GetUDPListener() == NULL) {
2806 //DebugLogWarning(_T("Ignored Kad Firewallrequest UDP because Kad is not running (%s)"), GetClientFullInfo());
2807 return;
2810 // first search if we know this IP already, if so the result might be biased and we need tell the requester
2811 bool errorAlreadyKnown = false;
2812 if (GetUploadState() != US_NONE || GetDownloadState() != DS_NONE || GetChatState() != MS_NONE) {
2813 errorAlreadyKnown = true;
2814 } else if (Kademlia::CKademlia::GetRoutingZone()->GetContact(wxUINT32_SWAP_ALWAYS(GetConnectIP()), 0, false) != NULL) {
2815 errorAlreadyKnown = true;
2818 uint16_t remoteInternPort = data->ReadUInt16();
2819 uint16_t remoteExternPort = data->ReadUInt16();
2820 uint32_t senderKey = data->ReadUInt32();
2821 if (remoteInternPort == 0) {
2822 //DebugLogError(_T("UDP Firewallcheck requested with Intern Port == 0 (%s)"), GetClientFullInfo());
2823 return;
2825 // if (senderKey == 0)
2826 // DebugLogWarning(_T("UDP Firewallcheck requested with SenderKey == 0 (%s)"), GetClientFullInfo());
2828 CMemFile testPacket1;
2829 testPacket1.WriteUInt8(errorAlreadyKnown ? 1 : 0);
2830 testPacket1.WriteUInt16(remoteInternPort);
2831 DebugSend(Kad2FirewallUDP, wxUINT32_SWAP_ALWAYS(GetConnectIP()), remoteInternPort);
2832 Kademlia::CKademlia::GetUDPListener()->SendPacket(testPacket1, KADEMLIA2_FIREWALLUDP, wxUINT32_SWAP_ALWAYS(GetConnectIP()), remoteInternPort, Kademlia::CKadUDPKey(senderKey, theApp->GetPublicIP(false)), NULL);
2834 // if the client has a router with PAT (and therefore a different extern port than intern), test this port too
2835 if (remoteExternPort != 0 && remoteExternPort != remoteInternPort) {
2836 CMemFile testPacket2;
2837 testPacket2.WriteUInt8(errorAlreadyKnown ? 1 : 0);
2838 testPacket2.WriteUInt16(remoteExternPort);
2839 DebugSend(Kad2FirewalledUDP, wxUINT32_SWAP_ALWAYS(GetConnectIP()), remoteExternPort);
2840 Kademlia::CKademlia::GetUDPListener()->SendPacket(testPacket2, KADEMLIA2_FIREWALLUDP, wxUINT32_SWAP_ALWAYS(GetConnectIP()), remoteExternPort, Kademlia::CKadUDPKey(senderKey, theApp->GetPublicIP(false)), NULL);
2842 //DebugLog(_T("Answered UDP Firewallcheck request (%s)"), GetClientFullInfo());
2845 void CUpDownClient::SetConnectOptions(uint8_t options, bool encryption, bool callback)
2847 SetCryptLayerSupport((options & 0x01) != 0 && encryption);
2848 SetCryptLayerRequest((options & 0x02) != 0 && encryption);
2849 SetCryptLayerRequires((options & 0x04) != 0 && encryption);
2850 SetDirectUDPCallbackSupport((options & 0x08) != 0 && callback);
2852 // File_checked_for_headers