2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
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
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.
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 "updownclient.h" // Needed for CUpDownClient
28 #include <protocol/Protocols.h>
29 #include <protocol/ed2k/Client2Client/TCP.h>
30 #include <protocol/ed2k/Client2Client/UDP.h>
31 #include <common/EventIDs.h>
32 #include <common/Macros.h>
33 #include <common/Constants.h>
36 #include <cmath> // Needed for std:exp
38 #include "ClientCredits.h" // Needed for CClientCredits
39 #include "ClientUDPSocket.h" // Needed for CClientUDPSocket
40 #include "DownloadQueue.h" // Needed for CDownloadQueue
41 #include "Preferences.h" // Needed for thePrefs
42 #include "Packet.h" // Needed for CPacket
43 #include "MemFile.h" // Needed for CMemFile
44 #include "ClientTCPSocket.h"// Needed for CClientTCPSocket
45 #include "ListenSocket.h" // Needed for CListenSocket
46 #include "amule.h" // Needed for theApp
47 #include "PartFile.h" // Needed for CPartFile
48 #include "SharedFileList.h"
49 #include "Statistics.h" // Needed for theStats
51 #include "GuiEvents.h" // Needed for Notify_*
52 #include "UploadQueue.h" // Needed for CUploadQueue
55 #ifdef __MULE_UNUSED_CODE__
56 // This function is left as a reminder.
57 // Changes here _must_ be reflected in CClientList::FindMatchingClient.
58 bool CUpDownClient::Compare(const CUpDownClient
* tocomp
, bool bIgnoreUserhash
) const
61 // should we wxASSERT here?
65 //Compare only the user hash..
66 if(!bIgnoreUserhash
&& HasValidHash() && tocomp
->HasValidHash()) {
67 return GetUserHash() == tocomp
->GetUserHash();
71 //User is firewalled.. Must do two checks..
72 if (GetIP()!=0 && GetIP() == tocomp
->GetIP()) {
73 //The IP of both match
74 if (GetUserPort()!=0 && GetUserPort() == tocomp
->GetUserPort()) {
78 if (GetKadPort()!=0 && GetKadPort() == tocomp
->GetKadPort()) {
84 if (GetUserIDHybrid()!=0
85 && GetUserIDHybrid() == tocomp
->GetUserIDHybrid()
87 && GetServerIP() == tocomp
->GetServerIP()
89 && GetServerPort() == tocomp
->GetServerPort()) {
90 //Both have the same lowID, Same serverIP and Port..
94 //Both IP, and Server do not match..
98 //User is not firewalled.
99 if (GetUserPort()!=0) {
100 //User has a Port, lets check the rest.
101 if (GetIP() != 0 && tocomp
->GetIP() != 0) {
102 //Both clients have a verified IP..
103 if(GetIP() == tocomp
->GetIP() && GetUserPort() == tocomp
->GetUserPort()) {
104 //IP and UserPort match..
108 //One of the two clients do not have a verified IP
109 if (GetUserIDHybrid() == tocomp
->GetUserIDHybrid() && GetUserPort() == tocomp
->GetUserPort()) {
110 //ID and Port Match..
116 if(GetKadPort()!=0) {
117 //User has a Kad Port.
118 if(GetIP() != 0 && tocomp
->GetIP() != 0) {
119 //Both clients have a verified IP.
120 if(GetIP() == tocomp
->GetIP() && GetKadPort() == tocomp
->GetKadPort()) {
121 //IP and KadPort Match..
125 //One of the users do not have a verified IP.
126 if (GetUserIDHybrid() == tocomp
->GetUserIDHybrid() && GetKadPort() == tocomp
->GetKadPort()) {
127 //ID and KadProt Match..
139 bool CUpDownClient::AskForDownload()
142 if (theApp
->listensocket
->TooManySockets()) {
144 if (GetDownloadState() != DS_TOOMANYCONNS
) {
145 SetDownloadState(DS_TOOMANYCONNS
);
148 } else if (!m_socket
->IsConnected()) {
149 if (GetDownloadState() != DS_TOOMANYCONNS
) {
150 SetDownloadState(DS_TOOMANYCONNS
);
155 m_bUDPPending
= false;
156 m_dwLastAskedTime
= ::GetTickCount();
157 SetDownloadState(DS_CONNECTING
);
158 SetSentCancelTransfer(0);
159 return TryToConnect();
163 void CUpDownClient::SendStartupLoadReq()
166 if (m_socket
==NULL
|| m_reqfile
==NULL
) {
169 SetDownloadState(DS_ONQUEUE
);
170 CMemFile
dataStartupLoadReq(16);
171 dataStartupLoadReq
.WriteHash(m_reqfile
->GetFileHash());
172 CPacket
* packet
= new CPacket(dataStartupLoadReq
, OP_EDONKEYPROT
, OP_STARTUPLOADREQ
);
173 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
174 AddDebugLogLineN(logLocalClient
, wxT("Local Client: OP_STARTUPLOADREQ to ") + GetFullIP());
175 SendPacket(packet
, true, true);
179 bool CUpDownClient::IsSourceRequestAllowed()
181 //#warning REWRITE - Source swapping from eMule.
183 uint32 dwTickCount
= ::GetTickCount() + CONNECTION_LATENCY
;
184 uint32 nTimePassedClient
= dwTickCount
- GetLastSrcAnswerTime();
185 uint32 nTimePassedFile
= dwTickCount
- m_reqfile
->GetLastAnsweredTime();
186 bool bNeverAskedBefore
= (GetLastAskedForSources() == 0);
188 uint32 uSources
= m_reqfile
->GetSourceCount();
190 // if client has the correct extended protocol
191 ExtProtocolAvailable() && (SupportsSourceExchange2() || GetSourceExchange1Version() > 1) &&
192 // AND if we need more sources
193 thePrefs::GetMaxSourcePerFileSoft() > uSources
&&
196 //source is not complete and file is very rare
198 && (bNeverAskedBefore
|| nTimePassedClient
> SOURCECLIENTREASKS
)
199 && (uSources
<= RARE_FILE
/5)
201 //source is not complete and file is rare
203 && (bNeverAskedBefore
|| nTimePassedClient
> SOURCECLIENTREASKS
)
204 && (uSources
<= RARE_FILE
|| uSources
- m_reqfile
->GetValidSourcesCount() <= RARE_FILE
/ 2)
205 && (nTimePassedFile
> SOURCECLIENTREASKF
)
207 // OR if file is not rare
208 ( (bNeverAskedBefore
|| nTimePassedClient
> (unsigned)(SOURCECLIENTREASKS
* MINCOMMONPENALTY
))
209 && (nTimePassedFile
> (unsigned)(SOURCECLIENTREASKF
* MINCOMMONPENALTY
))
216 void CUpDownClient::SendFileRequest()
218 wxCHECK_RET(m_reqfile
, wxT("Cannot request file when no reqfile is set"));
220 CMemFile
dataFileReq(16+16);
221 dataFileReq
.WriteHash(m_reqfile
->GetFileHash());
223 if (SupportMultiPacket()) {
224 DEBUG_ONLY( wxString sent_opcodes
; )
226 if (SupportExtMultiPacket()) {
227 dataFileReq
.WriteUInt64(m_reqfile
->GetFileSize());
230 AddDebugLogLineN(logClient
, wxT("Sending file request to client"));
232 dataFileReq
.WriteUInt8(OP_REQUESTFILENAME
);
233 DEBUG_ONLY( sent_opcodes
+= wxT("|RFNM|"); )
234 // Extended information
235 if (GetExtendedRequestsVersion() > 0) {
236 m_reqfile
->WritePartStatus(&dataFileReq
);
238 if (GetExtendedRequestsVersion() > 1) {
239 m_reqfile
->WriteCompleteSourcesCount(&dataFileReq
);
241 if (m_reqfile
->GetPartCount() > 1) {
242 DEBUG_ONLY( sent_opcodes
+= wxT("|RFID|"); )
243 dataFileReq
.WriteUInt8(OP_SETREQFILEID
);
245 if (IsEmuleClient()) {
246 SetRemoteQueueFull( true );
247 SetRemoteQueueRank(0);
249 if (IsSourceRequestAllowed()) {
250 if (SupportsSourceExchange2()){
251 DEBUG_ONLY( sent_opcodes
+= wxT("|RSRC2|"); )
252 dataFileReq
.WriteUInt8(OP_REQUESTSOURCES2
);
253 dataFileReq
.WriteUInt8(SOURCEEXCHANGE2_VERSION
);
254 const uint16 nOptions
= 0; // 16 ... Reserved
255 dataFileReq
.WriteUInt16(nOptions
);
257 DEBUG_ONLY( sent_opcodes
+= wxT("|RSRC|"); )
258 dataFileReq
.WriteUInt8(OP_REQUESTSOURCES
);
260 m_reqfile
->SetLastAnsweredTimeTimeout();
261 SetLastAskedForSources();
263 if (IsSupportingAICH()) {
264 DEBUG_ONLY( sent_opcodes
+= wxT("|AFHR|"); )
265 dataFileReq
.WriteUInt8(OP_AICHFILEHASHREQ
);
267 CPacket
* packet
= new CPacket(dataFileReq
, OP_EMULEPROT
, (SupportExtMultiPacket() ? OP_MULTIPACKET_EXT
: OP_MULTIPACKET
));
268 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
269 AddDebugLogLineN(logLocalClient
, CFormat(wxT("Local Client: %s (%s) to %s"))
270 % (SupportExtMultiPacket() ? wxT("OP_MULTIPACKET_EXT") : wxT("OP_MULTIPACKET")) % sent_opcodes
% GetFullIP());
271 SendPacket(packet
, true);
273 //This is extended information
274 if (GetExtendedRequestsVersion() > 0 ) {
275 m_reqfile
->WritePartStatus(&dataFileReq
);
277 if (GetExtendedRequestsVersion() > 1 ) {
278 m_reqfile
->WriteCompleteSourcesCount(&dataFileReq
);
280 CPacket
* packet
= new CPacket(dataFileReq
, OP_EDONKEYPROT
, OP_REQUESTFILENAME
);
281 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
282 AddDebugLogLineN( logLocalClient
, wxT("Local Client: OP_REQUESTFILENAME to ") + GetFullIP() );
283 SendPacket(packet
, true);
285 // 26-Jul-2003: removed requesting the file status for files <= PARTSIZE for better compatibility with ed2k protocol (eDonkeyHybrid).
286 // if the remote client answers the OP_REQUESTFILENAME with OP_REQFILENAMEANSWER the file is shared by the remote client. if we
287 // know that the file is shared, we know also that the file is complete and don't need to request the file status.
289 // Sending the packet could have deleted the client, check m_reqfile
290 if (m_reqfile
&& (m_reqfile
->GetPartCount() > 1)) {
291 CMemFile
dataSetReqFileID(16);
292 dataSetReqFileID
.WriteHash(m_reqfile
->GetFileHash());
293 packet
= new CPacket(dataSetReqFileID
, OP_EDONKEYPROT
, OP_SETREQFILEID
);
294 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
295 AddDebugLogLineN(logLocalClient
, wxT("Local Client: OP_SETREQFILEID to ") + GetFullIP());
296 SendPacket(packet
, true);
299 if (IsEmuleClient()) {
300 SetRemoteQueueFull( true );
301 SetRemoteQueueRank(0);
304 // Sending the packet could have deleted the client, check m_reqfile
305 if (m_reqfile
&& IsSourceRequestAllowed()) {
306 m_reqfile
->SetLastAnsweredTimeTimeout();
310 if (SupportsSourceExchange2()) {
311 packetdata
.WriteUInt8(SOURCEEXCHANGE2_VERSION
);
312 packetdata
.WriteUInt16(0 /* Reserved */);
315 packetdata
.WriteHash(m_reqfile
->GetFileHash());
317 packet
= new CPacket(packetdata
, OP_EMULEPROT
, SupportsSourceExchange2() ? OP_REQUESTSOURCES2
: OP_REQUESTSOURCES
);
319 theStats::AddUpOverheadSourceExchange(packet
->GetPacketSize());
320 AddDebugLogLineN( logLocalClient
, wxT("Local Client: OP_REQUESTSOURCES to ") + GetFullIP() );
321 SendPacket(packet
,true,true);
322 SetLastAskedForSources();
325 // Sending the packet could have deleted the client, check m_reqfile
326 if (m_reqfile
&& IsSupportingAICH()) {
327 packet
= new CPacket(OP_AICHFILEHASHREQ
,16,OP_EMULEPROT
);
328 packet
->Copy16ToDataBuffer((const char *)m_reqfile
->GetFileHash().GetHash());
329 theStats::AddUpOverheadOther(packet
->GetPacketSize());
330 AddDebugLogLineN(logLocalClient
, wxT("Local Client: OP_AICHFILEHASHREQ to ") + GetFullIP());
331 SendPacket(packet
,true,true);
337 void CUpDownClient::ProcessFileInfo(const CMemFile
* data
, const CPartFile
* file
)
341 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; file==NULL)"));
343 if (m_reqfile
==NULL
) {
344 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; m_reqfile==NULL)"));
346 if (file
!= m_reqfile
) {
347 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; m_reqfile!=file)"));
350 m_clientFilename
= data
->ReadString((GetUnicodeSupport() != utf8strNone
));
352 // 26-Jul-2003: removed requesting the file status for files <= PARTSIZE for better compatibility with ed2k protocol (eDonkeyHybrid).
353 // if the remote client answers the OP_REQUESTFILENAME with OP_REQFILENAMEANSWER the file is shared by the remote client. if we
354 // know that the file is shared, we know also that the file is complete and don't need to request the file status.
355 if (m_reqfile
->GetPartCount() == 1) {
356 m_nPartCount
= m_reqfile
->GetPartCount();
358 m_reqfile
->UpdatePartsFrequency( this, false ); // Decrement
359 m_downPartStatus
.setsize( m_nPartCount
, 1 );
360 m_reqfile
->UpdatePartsFrequency( this, true ); // Increment
362 m_bCompleteSource
= true;
364 UpdateDisplayedInfo();
365 // even if the file is <= PARTSIZE, we _may_ need the hashset for that file (if the file size == PARTSIZE)
366 if (m_reqfile
->IsHashSetNeeded()) {
368 CPacket
* packet
= new CPacket(OP_HASHSETREQUEST
,16, OP_EDONKEYPROT
);
369 packet
->Copy16ToDataBuffer((const char *)m_reqfile
->GetFileHash().GetHash());
370 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
371 AddDebugLogLineN(logLocalClient
, wxT("Local Client: OP_HASHSETREQUEST to ") + GetFullIP());
372 SendPacket(packet
,true,true);
373 SetDownloadState(DS_REQHASHSET
);
374 m_fHashsetRequesting
= 1;
375 m_reqfile
->SetHashSetNeeded(false);
380 SendStartupLoadReq();
382 m_reqfile
->UpdatePartsInfo();
386 void CUpDownClient::ProcessFileStatus(bool bUdpPacket
, const CMemFile
* data
, const CPartFile
* file
)
389 wxString
strReqFileNull(wxT("ERROR: Wrong file ID (ProcessFileStatus; m_reqfile==NULL)"));
391 if ( !m_reqfile
|| file
!= m_reqfile
){
393 throw strReqFileNull
;
395 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileStatus; m_reqfile!=file)"));
398 uint16 nED2KPartCount
= data
->ReadUInt16();
400 m_reqfile
->UpdatePartsFrequency( this, false ); // Decrement
401 m_downPartStatus
.clear();
403 bool bPartsNeeded
= false;
406 m_nPartCount
= m_reqfile
->GetPartCount();
407 m_downPartStatus
.setsize( m_nPartCount
, 1);
409 m_bCompleteSource
= true;
413 // Somehow this happened.
415 throw strReqFileNull
;
417 if (m_reqfile
->GetED2KPartCount() != nED2KPartCount
)
420 strError
<< wxT("ProcessFileStatus - wrong part number recv=") << nED2KPartCount
<<
421 wxT(" expected=") << m_reqfile
->GetED2KPartCount() << wxT(" ") <<
422 m_reqfile
->GetFileHash().Encode();
426 m_nPartCount
= m_reqfile
->GetPartCount();
428 m_bCompleteSource
= false;
429 m_downPartStatus
.setsize( m_nPartCount
, 0 );
433 while (done
!= m_nPartCount
) {
434 uint8 toread
= data
->ReadUInt8();
436 for ( uint8 i
= 0;i
< 8; i
++ ) {
437 bool status
= ((toread
>>i
)&1)? 1:0;
438 m_downPartStatus
.set(done
, status
);
441 if (!m_reqfile
->IsComplete(done
)){
446 if (done
== m_nPartCount
) {
452 // We want the counts to be updated, even if we fail to read everything
453 m_reqfile
->UpdatePartsFrequency( this, true ); // Increment
459 m_reqfile
->UpdatePartsFrequency( this, true ); // Increment
461 UpdateDisplayedInfo();
463 // NOTE: This function is invoked from TCP and UDP socket!
466 SetDownloadState(DS_NONEEDEDPARTS
);
467 } else if (m_reqfile
->IsHashSetNeeded()) {
468 //If we are using the eMule filerequest packets, this is taken care of in the Multipacket!
470 CPacket
* packet
= new CPacket(OP_HASHSETREQUEST
,16, OP_EDONKEYPROT
);
471 packet
->Copy16ToDataBuffer((const char *)m_reqfile
->GetFileHash().GetHash());
472 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
473 AddDebugLogLineN(logLocalClient
, wxT("Local Client: OP_HASHSETREQUEST to ") + GetFullIP());
474 SendPacket(packet
, true, true);
475 SetDownloadState(DS_REQHASHSET
);
476 m_fHashsetRequesting
= 1;
477 m_reqfile
->SetHashSetNeeded(false);
483 SendStartupLoadReq();
488 SetDownloadState(DS_NONEEDEDPARTS
);
490 SetDownloadState(DS_ONQUEUE
);
493 m_reqfile
->UpdatePartsInfo();
496 bool CUpDownClient::AddRequestForAnotherFile(CPartFile
* file
)
498 if ( m_A4AF_list
.find( file
) == m_A4AF_list
.end() ) {
499 // When we access a non-existing entry entry, it will be zeroed by default,
500 // so we have to set NeededParts. All in one go.
501 m_A4AF_list
[file
].NeededParts
= true;
502 file
->AddA4AFSource( this );
509 bool CUpDownClient::DeleteFileRequest(CPartFile
* file
)
511 return (m_A4AF_list
.erase( file
) > 0);
514 void CUpDownClient::DeleteAllFileRequests()
520 /* eMule 0.30c implementation, i give it a try (Creteil) BEGIN ... */
521 void CUpDownClient::SetDownloadState(uint8 byNewState
)
523 if (m_nDownloadState
!= byNewState
) {
525 // Notify the client that this source has changed its state
526 m_reqfile
->ClientStateChanged( m_nDownloadState
, byNewState
);
528 if (byNewState
== DS_DOWNLOADING
) {
529 m_reqfile
->AddDownloadingSource(this);
530 } else if (m_nDownloadState
== DS_DOWNLOADING
) {
531 m_reqfile
->RemoveDownloadingSource(this);
534 if (byNewState
== DS_DOWNLOADING
) {
535 msReceivedPrev
= GetTickCount();
536 theStats::AddDownloadingSource();
537 } else if (m_nDownloadState
== DS_DOWNLOADING
) {
538 theStats::RemoveDownloadingSource();
541 if (m_nDownloadState
== DS_DOWNLOADING
) {
542 m_nDownloadState
= byNewState
;
543 ClearDownloadBlockRequests();
546 bytesReceivedCycle
= 0;
548 if (byNewState
== DS_NONE
) {
550 m_reqfile
->UpdatePartsFrequency( this, false ); // Decrement
552 m_downPartStatus
.clear();
555 if (m_socket
&& byNewState
!= DS_ERROR
) {
556 m_socket
->DisableDownloadLimit();
559 m_nDownloadState
= byNewState
;
560 if(GetDownloadState() == DS_DOWNLOADING
) {
561 if (IsEmuleClient()) {
562 SetRemoteQueueFull(false);
564 SetRemoteQueueRank(0); // eMule 0.30c set like this ...
566 UpdateDisplayedInfo(true);
569 /* eMule 0.30c implementation, i give it a try (Creteil) END ... */
571 void CUpDownClient::ProcessHashSet(const uint8_t* packet
, uint32 size
)
573 if ((!m_reqfile
) || md4cmp(packet
,m_reqfile
->GetFileHash().GetHash())) {
574 throw wxString(wxT("Wrong fileid sent (ProcessHashSet)"));
576 if (!m_fHashsetRequesting
) {
577 throw wxString(wxT("Received unsolicited hashset, ignoring it."));
579 CMemFile
data(packet
,size
);
580 if (m_reqfile
->LoadHashsetFromFile(&data
,true)) {
581 m_fHashsetRequesting
= 0;
583 m_reqfile
->SetHashSetNeeded(true);
584 throw wxString(wxT("Corrupted or invalid hashset received"));
586 SendStartupLoadReq();
589 void CUpDownClient::SendBlockRequests()
591 uint32 current_time
= ::GetTickCount();
594 // Ask new blocks only when all completed
595 if (!m_PendingBlocks_list
.empty()) {
599 if ((m_dwLastBlockReceived
+ SEC2MS(5)) > current_time
) {
600 // We received last block in less than 5 secs? Let's request faster.
601 m_MaxBlockRequests
= m_MaxBlockRequests
<< 1;
602 if ( m_MaxBlockRequests
> 0x20) {
603 m_MaxBlockRequests
= 0x20;
606 m_MaxBlockRequests
= m_MaxBlockRequests
>> 1;
607 if ( m_MaxBlockRequests
< STANDARD_BLOCKS_REQUEST
) {
608 m_MaxBlockRequests
= STANDARD_BLOCKS_REQUEST
;
613 m_dwLastBlockReceived
= current_time
;
619 if (m_DownloadBlocks_list
.empty()) {
620 // Barry - instead of getting 3, just get how many is needed
621 uint16 count
= m_MaxBlockRequests
- m_PendingBlocks_list
.size();
622 std::vector
<Requested_Block_Struct
*> toadd
;
623 if (m_reqfile
->GetNextRequestedBlock(this, toadd
, count
)) {
624 for (int i
= 0; i
!= count
; i
++) {
625 m_DownloadBlocks_list
.push_back(toadd
[i
]);
630 // Barry - Why are unfinished blocks requested again, not just new ones?
632 while (m_PendingBlocks_list
.size() < m_MaxBlockRequests
&& !m_DownloadBlocks_list
.empty()) {
633 Pending_Block_Struct
* pblock
= new Pending_Block_Struct
;
634 pblock
->block
= m_DownloadBlocks_list
.front();
635 pblock
->zStream
= NULL
;
636 pblock
->totalUnzipped
= 0;
637 pblock
->fZStreamError
= 0;
638 pblock
->fRecovered
= 0;
639 m_PendingBlocks_list
.push_back(pblock
);
640 m_DownloadBlocks_list
.pop_front();
644 if (m_PendingBlocks_list
.empty()) {
646 CUpDownClient
* slower_client
= NULL
;
648 if (thePrefs::GetDropSlowSources()) {
649 slower_client
= m_reqfile
->GetSlowerDownloadingClient(m_lastaverage
, this);
652 if (slower_client
== NULL
) {
653 slower_client
= this;
656 if (!slower_client
->GetSentCancelTransfer()) {
657 CPacket
* packet
= new CPacket(OP_CANCELTRANSFER
, 0, OP_EDONKEYPROT
);
658 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
659 // if (slower_client != this) {
660 // printf("Dropped client %p to allow client %p to download\n",slower_client, this);
662 slower_client
->ClearDownloadBlockRequests();
663 slower_client
->SendPacket(packet
,true,true);
664 slower_client
->SetSentCancelTransfer(1);
667 slower_client
->SetDownloadState(DS_NONEEDEDPARTS
);
669 if (slower_client
!= this) {
670 // Re-request freed blocks.
671 AddDebugLogLineN( logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER (faster source eager to transfer) to ") + slower_client
->GetFullIP() );
672 wxASSERT(m_DownloadBlocks_list
.empty());
673 wxASSERT(m_PendingBlocks_list
.empty());
674 uint16 count
= m_MaxBlockRequests
;
675 std::vector
<Requested_Block_Struct
*> toadd
;
676 if (m_reqfile
->GetNextRequestedBlock(this, toadd
, count
)) {
677 for (int i
= 0; i
!= count
; i
++) {
678 Pending_Block_Struct
* pblock
= new Pending_Block_Struct
;
679 pblock
->block
= toadd
[i
];
680 pblock
->zStream
= NULL
;
681 pblock
->totalUnzipped
= 0;
682 pblock
->fZStreamError
= 0;
683 pblock
->fRecovered
= 0;
684 m_PendingBlocks_list
.push_back(pblock
);
687 // WTF, we just freed blocks.
688 wxFAIL_MSG(wxT("No free blocks to request after freeing some blocks"));
693 AddDebugLogLineN( logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER (no free blocks) to ") + GetFullIP() );
694 //#warning Kry - Would be nice to swap A4AF here.
699 CPacket
* packet
= NULL
;
703 // Most common scenario: hash + blocks to request + every one
704 // having 2 uint32 tags
706 uint8 nBlocks
= m_PendingBlocks_list
.size();
707 if (nBlocks
> m_MaxBlockRequests
) {
708 nBlocks
= m_MaxBlockRequests
;
711 CMemFile
data(16 + 1 + nBlocks
*((2+4)*2));
713 data
.WriteHash(m_reqfile
->GetFileHash());
715 data
.WriteUInt8(nBlocks
);
717 std::list
<Pending_Block_Struct
*>::iterator it
= m_PendingBlocks_list
.begin();
719 wxASSERT(it
!= m_PendingBlocks_list
.end());
720 wxASSERT( (*it
)->block
->StartOffset
<= (*it
)->block
->EndOffset
);
721 (*it
)->fZStreamError
= 0;
722 (*it
)->fRecovered
= 0;
723 CTagVarInt(/*Noname*/0,(*it
)->block
->StartOffset
).WriteTagToFile(&data
);
724 CTagVarInt(/*Noname*/0,(*it
)->block
->EndOffset
).WriteTagToFile(&data
);
729 packet
= new CPacket(data
, OP_ED2KV2HEADER
, OP_REQUESTPARTS
);
730 AddDebugLogLineN( logLocalClient
, CFormat(wxT("Local Client ED2Kv2: OP_REQUESTPARTS(%i) to %s"))
731 % (m_PendingBlocks_list
.size()<m_MaxBlockRequests
? m_PendingBlocks_list
.size() : m_MaxBlockRequests
) % GetFullIP() );
734 wxASSERT(m_MaxBlockRequests
== STANDARD_BLOCKS_REQUEST
);
736 //#warning Kry - I dont specially like this approach, we iterate one time too many
738 bool bHasLongBlocks
= false;
740 std::list
<Pending_Block_Struct
*>::iterator it
= m_PendingBlocks_list
.begin();
741 for (uint32 i
= 0; i
!= m_MaxBlockRequests
; i
++){
742 if (it
!= m_PendingBlocks_list
.end()) {
743 Pending_Block_Struct
* pending
= *it
++;
744 wxASSERT( pending
->block
->StartOffset
<= pending
->block
->EndOffset
);
745 if (pending
->block
->StartOffset
> 0xFFFFFFFF || pending
->block
->EndOffset
> 0xFFFFFFFF){
746 bHasLongBlocks
= true;
747 if (!SupportsLargeFiles()){
748 // Requesting a large block from a client that doesn't support large files?
749 if (!GetSentCancelTransfer()){
750 CPacket
* cancel_packet
= new CPacket(OP_CANCELTRANSFER
, 0, OP_EDONKEYPROT
);
751 theStats::AddUpOverheadFileRequest(cancel_packet
->GetPacketSize());
752 AddDebugLogLineN( logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
753 SendPacket(cancel_packet
,true,true);
754 SetSentCancelTransfer(1);
756 SetDownloadState(DS_ERROR
);
764 CMemFile
data(16 /*Hash*/ + (m_MaxBlockRequests
*(bHasLongBlocks
? 8 : 4) /* uint32/64 start*/) + (3*(bHasLongBlocks
? 8 : 4)/* uint32/64 end*/));
765 data
.WriteHash(m_reqfile
->GetFileHash());
767 it
= m_PendingBlocks_list
.begin();
768 for (uint32 i
= 0; i
!= m_MaxBlockRequests
; i
++) {
769 if (it
!= m_PendingBlocks_list
.end()) {
770 Pending_Block_Struct
* pending
= *it
++;
771 wxASSERT( pending
->block
->StartOffset
<= pending
->block
->EndOffset
);
772 pending
->fZStreamError
= 0;
773 pending
->fRecovered
= 0;
774 if (bHasLongBlocks
) {
775 data
.WriteUInt64(pending
->block
->StartOffset
);
777 data
.WriteUInt32(pending
->block
->StartOffset
);
780 if (bHasLongBlocks
) {
788 it
= m_PendingBlocks_list
.begin();
789 for (uint32 i
= 0; i
!= m_MaxBlockRequests
; i
++) {
790 if (it
!= m_PendingBlocks_list
.end()) {
791 Requested_Block_Struct
* block
= (*it
++)->block
;
792 if (bHasLongBlocks
) {
793 data
.WriteUInt64(block
->EndOffset
+1);
795 data
.WriteUInt32(block
->EndOffset
+1);
798 if (bHasLongBlocks
) {
805 packet
= new CPacket(data
, (bHasLongBlocks
? OP_EMULEPROT
: OP_EDONKEYPROT
), (bHasLongBlocks
? (uint8
)OP_REQUESTPARTS_I64
: (uint8
)OP_REQUESTPARTS
));
806 AddDebugLogLineN(logLocalClient
, CFormat(wxT("Local Client: %s to %s")) % (bHasLongBlocks
? wxT("OP_REQUESTPARTS_I64") : wxT("OP_REQUESTPARTS")) % GetFullIP());
810 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
811 SendPacket(packet
, true, true);
816 Barry - Originally this only wrote to disk when a full 180k block
817 had been received from a client, and only asked for data in
820 This meant that on average 90k was lost for every connection
821 to a client data source. That is a lot of wasted data.
823 To reduce the lost data, packets are now written to a buffer
824 and flushed to disk regularly regardless of size downloaded.
826 This includes compressed packets.
828 Data is also requested only where gaps are, not in 180k blocks.
829 The requests will still not exceed 180k, but may be smaller to
833 void CUpDownClient::ProcessBlockPacket(const uint8_t* packet
, uint32 size
, bool packed
, bool largeblocks
)
835 // Ignore if no data required
836 if (!(GetDownloadState() == DS_DOWNLOADING
|| GetDownloadState() == DS_NONEEDEDPARTS
)) {
840 // This vars are defined here to be able to use them on the catch
841 int header_size
= 16;
842 uint64 nStartPos
= 0;
844 uint32 nBlockSize
= 0;
845 uint32 lenUnzipped
= 0;
848 m_dwLastBlockReceived
= ::GetTickCount();
852 // Read data from packet
853 const CMemFile
data(packet
, size
);
855 // Check that this data is for the correct file
856 if ((!m_reqfile
) || data
.ReadHash() != m_reqfile
->GetFileHash()) {
857 throw wxString(wxT("Wrong fileid sent (ProcessBlockPacket)"));
860 // Find the start & end positions, and size of this chunk of data
863 nStartPos
= data
.ReadUInt64();
866 nStartPos
= data
.ReadUInt32();
871 nBlockSize
= data
.ReadUInt32();
873 nEndPos
= nStartPos
+ (size
- header_size
);
876 nEndPos
= data
.ReadUInt64();
879 nEndPos
= data
.ReadUInt32();
884 // Check that packet size matches the declared data size + header size
885 if ( nEndPos
== nStartPos
|| size
!= ((nEndPos
- nStartPos
) + header_size
)) {
886 throw wxString(wxT("Corrupted or invalid DataBlock received (ProcessBlockPacket)"));
888 theStats::AddDownloadFromSoft(GetClientSoft(),size
- header_size
);
889 bytesReceivedCycle
+= size
- header_size
;
891 credits
->AddDownloaded(size
- header_size
, GetIP(), theApp
->CryptoAvailable());
893 // Move end back one, should be inclusive
896 // Loop through to find the reserved block that this is within
897 std::list
<Pending_Block_Struct
*>::iterator it
= m_PendingBlocks_list
.begin();
898 for (; it
!= m_PendingBlocks_list
.end(); ++it
) {
899 Pending_Block_Struct
* cur_block
= *it
;
901 if ((cur_block
->block
->StartOffset
<= nStartPos
) && (cur_block
->block
->EndOffset
>= nStartPos
)) {
902 // Found reserved block
904 if (cur_block
->block
->StartOffset
== nStartPos
) {
905 // This block just started transfering. Set the start time.
906 m_last_block_start
= ::GetTickCountFullRes();
909 if (cur_block
->fZStreamError
){
910 AddDebugLogLineN(logZLib
,
911 CFormat(wxT("Ignoring %u bytes of block %u-%u because of erroneous zstream state for file: %s"))
912 % (size
- header_size
) % nStartPos
% nEndPos
% m_reqfile
->GetFileName());
913 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
917 // Remember this start pos, used to draw part downloading in list
918 m_lastDownloadingPart
= nStartPos
/ PARTSIZE
;
920 // Occasionally packets are duplicated, no point writing it twice
921 // This will be 0 in these cases, or the length written otherwise
922 uint32 lenWritten
= 0;
924 // Handle differently depending on whether packed or not
926 // security sanitize check
927 if (nEndPos
> cur_block
->block
->EndOffset
) {
928 AddDebugLogLineN(logRemoteClient
, CFormat(wxT("Received Blockpacket exceeds requested boundaries (requested end: %u, Part: %u, received end: %u, Part: %u), file: %s remote IP: %s")) % cur_block
->block
->EndOffset
% (uint32
)(cur_block
->block
->EndOffset
/ PARTSIZE
) % nEndPos
% (uint32
)(nEndPos
/ PARTSIZE
) % m_reqfile
->GetFileName() % Uint32toStringIP(GetIP()));
929 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
932 // Write to disk (will be buffered in part file class)
933 lenWritten
= m_reqfile
->WriteToBuffer( size
- header_size
, (uint8_t*)(packet
+ header_size
), nStartPos
, nEndPos
, cur_block
->block
, this);
936 wxASSERT( (long int)size
> 0 );
937 // Create space to store unzipped data, the size is
938 // only an initial guess, will be resized in unzip()
940 lenUnzipped
= (size
* 2);
942 if (lenUnzipped
> (BLOCKSIZE
+ 300)) {
943 lenUnzipped
= (BLOCKSIZE
+ 300);
945 uint8_t *unzipped
= new uint8_t[lenUnzipped
];
947 // Try to unzip the packet
948 int result
= unzip(cur_block
, (uint8_t*)(packet
+ header_size
), (size
- header_size
), &unzipped
, &lenUnzipped
);
950 // no block can be uncompressed to >2GB, 'lenUnzipped' is obviously erroneous.
951 if (result
== Z_OK
&& ((int)lenUnzipped
>= 0)) {
953 // Write any unzipped data to disk
954 if (lenUnzipped
> 0) {
955 wxASSERT( (int)lenUnzipped
> 0 );
957 // Use the current start and end positions for the uncompressed data
958 nStartPos
= cur_block
->block
->StartOffset
+ cur_block
->totalUnzipped
- lenUnzipped
;
959 nEndPos
= cur_block
->block
->StartOffset
+ cur_block
->totalUnzipped
- 1;
961 if (nStartPos
> cur_block
->block
->EndOffset
|| nEndPos
> cur_block
->block
->EndOffset
) {
962 AddDebugLogLineN(logZLib
,
963 CFormat(wxT("Corrupted compressed packet for '%s' received (error 666)")) % m_reqfile
->GetFileName());
964 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
966 // Write uncompressed data to file
967 lenWritten
= m_reqfile
->WriteToBuffer( size
- header_size
,
976 wxString strZipError
;
977 if (cur_block
->zStream
&& cur_block
->zStream
->msg
) {
978 strZipError
= wxT(" - ") + wxString::FromAscii(cur_block
->zStream
->msg
);
981 AddDebugLogLineN(logZLib
,
982 CFormat(wxT("Corrupted compressed packet for '%s' received (error %i): %s"))
983 % m_reqfile
->GetFileName() % result
% strZipError
);
985 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
987 // If we had an zstream error, there is no chance that we could recover from it nor that we
988 // could use the current zstream (which is in error state) any longer.
989 if (cur_block
->zStream
){
990 inflateEnd(cur_block
->zStream
);
991 delete cur_block
->zStream
;
992 cur_block
->zStream
= NULL
;
995 // Although we can't further use the current zstream, there is no need to disconnect the sending
996 // client because the next zstream (a series of 10K-blocks which build a 180K-block) could be
997 // valid again. Just ignore all further blocks for the current zstream.
998 cur_block
->fZStreamError
= 1;
999 cur_block
->totalUnzipped
= 0; // bluecow's fix
1003 // These checks only need to be done if any data was written
1004 if (lenWritten
> 0) {
1005 m_nTransferredDown
+= lenWritten
;
1007 // If finished reserved block
1008 if (nEndPos
== cur_block
->block
->EndOffset
) {
1010 // Save last average speed based on data and time.
1011 // This should do bytes/sec.
1012 uint32 average_time
= (::GetTickCountFullRes() - m_last_block_start
);
1014 // Avoid divide by 0.
1015 if (average_time
== 0) {
1019 m_lastaverage
= ((cur_block
->block
->EndOffset
- cur_block
->block
->StartOffset
) * 1000) / average_time
;
1021 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
1022 delete cur_block
->block
;
1023 // Not always allocated
1024 if (cur_block
->zStream
) {
1025 inflateEnd(cur_block
->zStream
);
1026 delete cur_block
->zStream
;
1029 m_PendingBlocks_list
.erase(it
);
1031 // Request next block
1032 SendBlockRequests();
1035 // Stop looping and exit method
1039 } catch (const CEOFException
& e
) {
1040 wxString error
= wxString(wxT("Error reading "));
1041 if (packed
) error
+= CFormat(wxT("packed (LU: %i) largeblocks ")) % lenUnzipped
;
1042 error
+= CFormat(wxT("data packet: RS: %i HS: %i SP: %i EP: %i BS: %i -> "))
1043 % size
% header_size
% nStartPos
% nEndPos
% nBlockSize
;
1044 AddDebugLogLineC(logRemoteClient
, error
+ e
.what());
1049 int CUpDownClient::unzip(Pending_Block_Struct
*block
, uint8_t *zipped
, uint32 lenZipped
, uint8_t **unzipped
, uint32
*lenUnzipped
, int iRecursion
)
1051 int err
= Z_DATA_ERROR
;
1054 z_stream
*zS
= block
->zStream
;
1056 // Is this the first time this block has been unzipped
1059 block
->zStream
= new z_stream
;
1060 zS
= block
->zStream
;
1062 // Initialise stream values
1063 zS
->zalloc
= (alloc_func
)0;
1064 zS
->zfree
= (free_func
)0;
1065 zS
->opaque
= (voidpf
)0;
1067 // Set output data streams, do this here to avoid overwriting on recursive calls
1068 zS
->next_out
= (*unzipped
);
1069 zS
->avail_out
= (*lenUnzipped
);
1071 // Initialise the z_stream
1072 err
= inflateInit(zS
);
1078 // Use whatever input is provided
1079 zS
->next_in
= zipped
;
1080 zS
->avail_in
= lenZipped
;
1082 // Only set the output if not being called recursively
1083 if (iRecursion
== 0) {
1084 zS
->next_out
= (*unzipped
);
1085 zS
->avail_out
= (*lenUnzipped
);
1088 // Try to unzip the data
1089 err
= inflate(zS
, Z_SYNC_FLUSH
);
1091 // Is zip finished reading all currently available input and writing
1092 // all generated output
1093 if (err
== Z_STREAM_END
) {
1095 err
= inflateEnd(zS
);
1100 // Got a good result, set the size to the amount unzipped in this call
1101 // (including all recursive calls)
1102 (*lenUnzipped
) = (zS
->total_out
- block
->totalUnzipped
);
1103 block
->totalUnzipped
= zS
->total_out
;
1104 } else if ((err
== Z_OK
) && (zS
->avail_out
== 0) && (zS
->avail_in
!= 0)) {
1106 // Output array was not big enough,
1107 // call recursively until there is enough space
1109 // What size should we try next
1110 uint32 newLength
= (*lenUnzipped
) *= 2;
1111 if (newLength
== 0) {
1112 newLength
= lenZipped
* 2;
1114 // Copy any data that was successfully unzipped to new array
1115 uint8_t *temp
= new uint8_t[newLength
];
1116 wxASSERT( zS
->total_out
- block
->totalUnzipped
<= newLength
);
1117 memcpy(temp
, (*unzipped
), (zS
->total_out
- block
->totalUnzipped
));
1118 delete [] (*unzipped
);
1120 (*lenUnzipped
) = newLength
;
1122 // Position stream output to correct place in new array
1123 zS
->next_out
= (*unzipped
) + (zS
->total_out
- block
->totalUnzipped
);
1124 zS
->avail_out
= (*lenUnzipped
) - (zS
->total_out
- block
->totalUnzipped
);
1127 err
= unzip(block
, zS
->next_in
, zS
->avail_in
, unzipped
, lenUnzipped
, iRecursion
+ 1);
1128 } else if ((err
== Z_OK
) && (zS
->avail_in
== 0)) {
1129 // All available input has been processed, everything ok.
1130 // Set the size to the amount unzipped in this call
1131 // (including all recursive calls)
1132 (*lenUnzipped
) = (zS
->total_out
- block
->totalUnzipped
);
1133 block
->totalUnzipped
= zS
->total_out
;
1135 // Should not get here unless input data is corrupt
1136 wxString strZipError
;
1139 strZipError
= CFormat(wxT(" %d '%s'")) % err
% wxString::FromAscii(zS
->msg
);
1140 } else if (err
!= Z_OK
) {
1141 strZipError
= CFormat(wxT(" %d")) % err
;
1144 AddDebugLogLineN(logZLib
,
1145 CFormat(wxT("Unexpected zip error %s in file '%s'"))
1146 % strZipError
% (m_reqfile
? m_reqfile
->GetFileName() : CPath(wxT("?"))));
1157 // Speed is now updated only when data was received, calculated as
1158 // (data received) / (time since last receiption)
1159 // and slightly filtered (10s average).
1160 // Result is quite precise now and makes the DownloadRateAdjust workaround obsolete.
1162 float CUpDownClient::CalculateKBpsDown()
1164 const float tAverage
= 10.0;
1165 uint32 msCur
= GetTickCount();
1167 if (bytesReceivedCycle
) {
1168 float dt
= (msCur
- msReceivedPrev
) / 1000.0; // time since last reception
1169 if (dt
< 0.01) { // (safeguard against divide-by-zero)
1170 dt
= 0.01f
; // diff should be 100ms actually
1172 float kBpsDownCur
= bytesReceivedCycle
/ 1024.0 / dt
;
1173 if (dt
>= tAverage
) {
1174 kBpsDown
= kBpsDownCur
;
1176 kBpsDown
= (kBpsDown
* (tAverage
- dt
) + kBpsDownCur
* dt
) / tAverage
;
1178 //AddDebugLogLineN(logLocalClient, CFormat(wxT("CalculateKBpsDown %p kbps %.1f kbpsCur %.1f dt %.3f rcv %d "))
1179 // % this % kBpsDown % kBpsDownCur % dt % bytesReceivedCycle);
1180 bytesReceivedCycle
= 0;
1181 msReceivedPrev
= msCur
;
1185 if (m_cShowDR
== 30){
1187 UpdateDisplayedInfo();
1189 if (msCur
- m_dwLastBlockReceived
> DOWNLOADTIMEOUT
) {
1190 if (!GetSentCancelTransfer()){
1191 CPacket
* packet
= new CPacket(OP_CANCELTRANSFER
, 0, OP_EDONKEYPROT
);
1192 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
1193 AddDebugLogLineN( logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
1194 SendPacket(packet
,true,true);
1195 SetSentCancelTransfer(1);
1197 SetDownloadState(DS_ONQUEUE
);
1203 uint16
CUpDownClient::GetAvailablePartCount() const
1206 for (int i
= 0;i
!= m_nPartCount
;i
++){
1207 if (IsPartAvailable(i
))
1213 void CUpDownClient::SetRemoteQueueRank(uint16 nr
)
1215 m_nOldRemoteQueueRank
= m_nRemoteQueueRank
;
1216 m_nRemoteQueueRank
= nr
;
1217 UpdateDisplayedInfo();
1220 void CUpDownClient::UDPReaskACK(uint16 nNewQR
)
1223 m_bUDPPending
= false;
1224 SetRemoteQueueRank(nNewQR
);
1225 m_dwLastAskedTime
= ::GetTickCount();
1228 void CUpDownClient::UDPReaskFNF()
1230 m_bUDPPending
= false;
1232 // avoid premature deletion of 'this' client
1233 if (GetDownloadState() != DS_DOWNLOADING
){
1235 m_reqfile
->AddDeadSource(this);
1238 theApp
->downloadqueue
->RemoveSource(this);
1240 if (Disconnected(wxT("UDPReaskFNF m_socket=NULL"))) {
1245 AddDebugLogLineN( logRemoteClient
, wxT("UDP ANSWER FNF : ") + GetUserName() + wxT(" - did not remove client because of current download state") );
1249 void CUpDownClient::UDPReaskForDownload()
1252 wxASSERT(m_reqfile
);
1254 if(!m_reqfile
|| m_bUDPPending
) {
1258 //#warning We should implement the quality tests for udp reliability
1260 if( m_nTotalUDPPackets > 3 && ((float)(m_nFailedUDPPackets/m_nTotalUDPPackets) > .3)) {
1265 if (thePrefs::GetEffectiveUDPPort() == 0) {
1269 if (m_nUDPPort
!= 0 && !theApp
->IsFirewalled() && !IsConnected()) {
1270 //don't use udp to ask for sources
1271 if(IsSourceRequestAllowed()) {
1275 m_bUDPPending
= true;
1278 data
.WriteHash(m_reqfile
->GetFileHash());
1280 if (GetUDPVersion() > 3) {
1281 if (m_reqfile
->IsPartFile()) {
1282 static_cast<CPartFile
*>(m_reqfile
)->WritePartStatus(&data
);
1285 data
.WriteUInt16(0);
1289 if (GetUDPVersion() > 2) {
1290 data
.WriteUInt16(m_reqfile
->m_nCompleteSourcesCount
);
1293 CPacket
* response
= new CPacket(data
, OP_EMULEPROT
, OP_REASKFILEPING
);
1294 AddDebugLogLineN( logClientUDP
, wxT("Client UDP socket: send OP_REASKFILEPING") );
1295 theStats::AddUpOverheadFileRequest(response
->GetPacketSize());
1296 theApp
->clientudp
->SendPacket(response
,GetConnectIP(),GetUDPPort(), ShouldReceiveCryptUDPPackets(), GetUserHash().GetHash(), false, 0);
1297 } else if (HasLowID() && GetBuddyIP() && GetBuddyPort() && HasValidBuddyID()) {
1299 m_bUDPPending
= true;
1303 data
.WriteHash(CMD4Hash(GetBuddyID()));
1304 data
.WriteHash(m_reqfile
->GetFileHash());
1306 if (GetUDPVersion() > 3) {
1307 if (m_reqfile
->IsPartFile()) {
1308 static_cast<CPartFile
*>(m_reqfile
)->WritePartStatus(&data
);
1310 data
.WriteUInt16(0);
1314 if (GetUDPVersion() > 2) {
1315 data
.WriteUInt16(m_reqfile
->m_nCompleteSourcesCount
);
1318 CPacket
* response
= new CPacket(data
, OP_EMULEPROT
, OP_REASKCALLBACKUDP
);
1319 AddDebugLogLineN( logClientUDP
, wxT("Client UDP socket: send OP_REASKCALLBACKUDP") );
1320 theStats::AddUpOverheadFileRequest(response
->GetPacketSize());
1321 theApp
->clientudp
->SendPacket(response
, GetBuddyIP(), GetBuddyPort(), false, NULL
, true, 0 );
1326 // Get the next part that is requested
1327 uint16
CUpDownClient::GetNextRequestedPart() const
1329 uint16 part
= 0xffff;
1331 std::list
<Pending_Block_Struct
*>::const_iterator it
= m_PendingBlocks_list
.begin();
1332 for (; it
!= m_PendingBlocks_list
.end(); ++it
) {
1333 part
= (*it
)->block
->StartOffset
/ PARTSIZE
;
1334 if (part
!= m_lastDownloadingPart
) {
1343 void CUpDownClient::UpdateDisplayedInfo(bool force
)
1345 uint32 curTick
= ::GetTickCount();
1346 if (force
|| curTick
-m_lastRefreshedDLDisplay
> MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE
) {
1347 // Check if we actually need to notify of changes
1348 bool update
= m_reqfile
&& m_reqfile
->ShowSources();
1350 // Check A4AF files only if needed
1352 A4AFList::iterator it
= m_A4AF_list
.begin();
1353 for ( ; it
!= m_A4AF_list
.end(); ++it
) {
1354 if ( it
->first
->ShowSources() ) {
1361 // And finnaly trigger an event if there's any reason
1363 SourceItemType type
;
1364 switch (GetDownloadState()) {
1365 case DS_DOWNLOADING
:
1367 // We will send A4AF, which will be checked.
1371 type
= UNAVAILABLE_SOURCE
;
1375 Notify_SourceCtrlUpdateSource(ECID(), type
);
1378 // Shared files view
1379 if (m_uploadingfile
&& m_uploadingfile
->ShowPeers()) {
1380 Notify_SharedCtrlRefreshClient(ECID(), AVAILABLE_SOURCE
);
1383 m_lastRefreshedDLDisplay
= curTick
;
1387 uint8
CUpDownClient::GetObfuscationStatus() const
1389 uint8 ret
= OBST_UNDEFINED
;
1390 if (thePrefs::IsClientCryptLayerSupported()) {
1391 if (SupportsCryptLayer()) {
1392 if ((RequestsCryptLayer() || thePrefs::IsClientCryptLayerRequested()) && HasObfuscatedConnectionBeenEstablished()) {
1395 ret
= OBST_SUPPORTED
;
1398 ret
= OBST_NOT_SUPPORTED
;
1401 ret
= OBST_DISABLED
;
1406 // IgnoreNoNeeded = will switch to files of which this source has no needed parts (if no better fiels found)
1407 // ignoreSuspensions = ignore timelimit for A4Af jumping
1408 // bRemoveCompletely = do not readd the file which the source is swapped from to the A4AF lists (needed if deleting or stopping a file)
1409 // toFile = Try to swap to this partfile only
1411 bool CUpDownClient::SwapToAnotherFile(bool bIgnoreNoNeeded
, bool ignoreSuspensions
, bool bRemoveCompletely
, CPartFile
* toFile
)
1413 // Fail if m_reqfile is invalid
1414 if ( m_reqfile
== NULL
) {
1418 // It would be stupid to swap away a downloading source
1419 if (GetDownloadState() == DS_DOWNLOADING
) {
1423 // The iterator of the final target
1424 A4AFList::iterator target
= m_A4AF_list
.end();
1426 // Do we want to swap to a specific file?
1427 if ( toFile
!= NULL
) {
1428 A4AFList::iterator it
= m_A4AF_list
.find( toFile
);
1429 if ( it
!= m_A4AF_list
.end() ) {
1431 // We force ignoring of timestamps
1432 if ( IsValidSwapTarget( it
, bIgnoreNoNeeded
, true ) ) {
1438 // We want highest priority possible, but need to start with
1439 // a value less than any other priority
1442 A4AFList::iterator it
= m_A4AF_list
.begin();
1443 for ( ; it
!= m_A4AF_list
.end(); ++it
) {
1444 if ( IsValidSwapTarget( it
, bIgnoreNoNeeded
, ignoreSuspensions
) ) {
1445 char cur_priority
= it
->first
->GetDownPriority();
1447 // We would prefer to get files with needed parts, thus rate them higher.
1448 // However, this really only matters if bIgnoreNoNeeded is true.
1449 if ( it
->second
.NeededParts
)
1452 // Change target if the current file has a higher rate than the previous
1453 if ( cur_priority
> priority
) {
1454 priority
= cur_priority
;
1456 // Set the new target
1459 // Break on the first High-priority file with needed parts
1460 if ( priority
== PR_HIGH
+ 10 ) {
1468 // Try to swap if we found a valid target
1469 if ( target
!= m_A4AF_list
.end() ) {
1471 // Sanity check, if reqfile doesn't own the source, then something
1472 // is wrong and the swap cannot proceed.
1473 if ( m_reqfile
->DelSource( this ) ) {
1474 CPartFile
* SwapTo
= target
->first
;
1476 // remove this client from the A4AF list of our new m_reqfile
1477 if ( SwapTo
->RemoveA4AFSource( this ) ) {
1478 Notify_SourceCtrlRemoveSource(ECID(), SwapTo
);
1481 m_reqfile
->RemoveDownloadingSource( this );
1483 // Do we want to remove it completly? Say if the old file is getting deleted
1484 if ( !bRemoveCompletely
) {
1485 m_reqfile
->AddA4AFSource( this );
1487 // Set the status of the old file
1488 m_A4AF_list
[m_reqfile
].NeededParts
= (GetDownloadState() != DS_NONEEDEDPARTS
);
1490 // Avoid swapping to this file for a while
1491 m_A4AF_list
[m_reqfile
].timestamp
= ::GetTickCount();
1493 Notify_SourceCtrlAddSource(m_reqfile
, CCLIENTREF(this, wxT("CUpDownClient::SwapToAnotherFile Notify_SourceCtrlAddSource 1")), A4AF_SOURCE
);
1495 Notify_SourceCtrlRemoveSource(ECID(), m_reqfile
);
1498 SetDownloadState(DS_NONE
);
1499 ResetFileStatusInfo();
1501 m_nRemoteQueueRank
= 0;
1502 m_nOldRemoteQueueRank
= 0;
1504 m_reqfile
->UpdatePartsInfo();
1506 SetRequestFile( SwapTo
);
1508 SwapTo
->AddSource( this );
1510 Notify_SourceCtrlAddSource(SwapTo
, CCLIENTREF(this, wxT("CUpDownClient::SwapToAnotherFile Notify_SourceCtrlAddSource 2")), UNAVAILABLE_SOURCE
);
1512 // Remove the new reqfile from the list of other files
1513 m_A4AF_list
.erase( target
);
1523 bool CUpDownClient::IsValidSwapTarget( A4AFList::iterator it
, bool ignorenoneeded
, bool ignoresuspended
)
1525 wxASSERT( it
!= m_A4AF_list
.end() && it
->first
);
1527 // Check if this file has been suspended
1528 if ( !ignoresuspended
) {
1529 if ( ::GetTickCount() - it
->second
.timestamp
>= PURGESOURCESWAPSTOP
) {
1530 // The wait-time has been exceeded and the file is now a valid target
1531 it
->second
.timestamp
= 0;
1533 // The file was still suspended and we are not ignoring suspensions
1538 // Check if the client has needed parts
1539 if ( !ignorenoneeded
) {
1540 if ( !it
->second
.NeededParts
) {
1545 // Final checks to see if the client is a valid target
1546 CPartFile
* cur_file
= it
->first
;
1547 if ( ( cur_file
!= m_reqfile
&& !cur_file
->IsStopped() ) &&
1548 ( cur_file
->GetStatus() == PS_READY
|| cur_file
->GetStatus() == PS_EMPTY
) &&
1549 ( cur_file
->IsPartFile() ) )
1558 void CUpDownClient::SetRequestFile(CPartFile
* reqfile
)
1560 if ( m_reqfile
!= reqfile
) {
1561 // Decrement the source-count of the old request-file
1563 m_reqfile
->ClientStateChanged( GetDownloadState(), -1 );
1564 m_reqfile
->UpdatePartsFrequency( this, false );
1568 m_downPartStatus
.clear();
1570 m_reqfile
= reqfile
;
1573 // Increment the source-count of the new request-file
1574 m_reqfile
->ClientStateChanged( -1, GetDownloadState() );
1576 m_nPartCount
= reqfile
->GetPartCount();
1581 void CUpDownClient::SetReqFileAICHHash(CAICHHash
* val
){
1582 if(m_pReqFileAICHHash
!= NULL
&& m_pReqFileAICHHash
!= val
)
1583 delete m_pReqFileAICHHash
;
1584 m_pReqFileAICHHash
= val
;
1587 void CUpDownClient::SendAICHRequest(CPartFile
* pForFile
, uint16 nPart
){
1588 CAICHRequestedData request
;
1589 request
.m_nPart
= nPart
;
1590 request
.m_pClient
.Link(this CLIENT_DEBUGSTRING("CUpDownClient::SendAICHRequest"));
1591 request
.m_pPartFile
= pForFile
;
1592 CAICHHashSet::m_liRequestedData
.push_back(request
);
1593 m_fAICHRequested
= TRUE
;
1595 data
.WriteHash(pForFile
->GetFileHash());
1596 data
.WriteUInt16(nPart
);
1597 pForFile
->GetAICHHashset()->GetMasterHash().Write(&data
);
1598 CPacket
* packet
= new CPacket(data
, OP_EMULEPROT
, OP_AICHREQUEST
);
1599 theStats::AddUpOverheadOther(packet
->GetPacketSize());
1600 AddDebugLogLineN(logLocalClient
, wxT("Local Client: OP_AICHREQUEST to") + GetFullIP());
1601 SafeSendPacket(packet
);
1604 void CUpDownClient::ProcessAICHAnswer(const uint8_t* packet
, uint32 size
)
1606 if (m_fAICHRequested
== FALSE
){
1607 throw wxString(wxT("Received unrequested AICH Packet"));
1609 m_fAICHRequested
= FALSE
;
1611 CMemFile
data(packet
, size
);
1613 CAICHHashSet::ClientAICHRequestFailed(this);
1617 CMD4Hash hash
= data
.ReadHash();
1618 CPartFile
* pPartFile
= theApp
->downloadqueue
->GetFileByID(hash
);
1619 CAICHRequestedData request
= CAICHHashSet::GetAICHReqDetails(this);
1620 uint16 nPart
= data
.ReadUInt16();
1621 if (pPartFile
!= NULL
&& request
.m_pPartFile
== pPartFile
&& request
.m_pClient
.GetClient() == this && nPart
== request
.m_nPart
){
1622 CAICHHash
ahMasterHash(&data
);
1623 if ( (pPartFile
->GetAICHHashset()->GetStatus() == AICH_TRUSTED
|| pPartFile
->GetAICHHashset()->GetStatus() == AICH_VERIFIED
)
1624 && ahMasterHash
== pPartFile
->GetAICHHashset()->GetMasterHash())
1626 if(pPartFile
->GetAICHHashset()->ReadRecoveryData(request
.m_nPart
*PARTSIZE
, &data
)){
1627 // finally all checks passed, everythings seem to be fine
1628 AddDebugLogLineN(logAICHTransfer
, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1629 CAICHHashSet::RemoveClientAICHRequest(this);
1630 pPartFile
->AICHRecoveryDataAvailable(request
.m_nPart
);
1633 AddDebugLogLineN(logAICHTransfer
, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1636 AddDebugLogLineN( logAICHTransfer
, wxT("AICH Packet Answer: Masterhash differs from packethash or hashset has no trusted Masterhash") );
1639 AddDebugLogLineN( logAICHTransfer
, wxT("AICH Packet Answer: requested values differ from values in packet") );
1642 CAICHHashSet::ClientAICHRequestFailed(this);
1646 void CUpDownClient::ProcessAICHRequest(const uint8_t* packet
, uint32 size
)
1648 if (size
!= 16 + 2 + CAICHHash::GetHashSize()) {
1649 throw wxString(wxT("Received AICH Request Packet with wrong size"));
1652 CMemFile
data(packet
, size
);
1654 CMD4Hash hash
= data
.ReadHash();
1655 uint16 nPart
= data
.ReadUInt16();
1656 CAICHHash
ahMasterHash(&data
);
1657 CKnownFile
* pKnownFile
= theApp
->sharedfiles
->GetFileByID(hash
);
1658 if (pKnownFile
!= NULL
){
1659 if (pKnownFile
->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE
&& pKnownFile
->GetAICHHashset()->HasValidMasterHash()
1660 && pKnownFile
->GetAICHHashset()->GetMasterHash() == ahMasterHash
&& pKnownFile
->GetPartCount() > nPart
1661 && pKnownFile
->GetFileSize() > EMBLOCKSIZE
&& pKnownFile
->GetFileSize() - PARTSIZE
*nPart
> EMBLOCKSIZE
)
1663 CMemFile fileResponse
;
1664 fileResponse
.WriteHash(pKnownFile
->GetFileHash());
1665 fileResponse
.WriteUInt16(nPart
);
1666 pKnownFile
->GetAICHHashset()->GetMasterHash().Write(&fileResponse
);
1667 if (pKnownFile
->GetAICHHashset()->CreatePartRecoveryData(nPart
*PARTSIZE
, &fileResponse
)){
1668 AddDebugLogLineN(logAICHTransfer
,
1669 CFormat(wxT("AICH Packet Request: Sucessfully created and send recoverydata for '%s' to %s"))
1670 % pKnownFile
->GetFileName() % GetClientFullInfo());
1672 CPacket
* packAnswer
= new CPacket(fileResponse
, OP_EMULEPROT
, OP_AICHANSWER
);
1673 theStats::AddUpOverheadOther(packAnswer
->GetPacketSize());
1674 AddDebugLogLineN(logLocalClient
, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1675 SafeSendPacket(packAnswer
);
1678 AddDebugLogLineN(logAICHTransfer
,
1679 CFormat(wxT("AICH Packet Request: Failed to create recoverydata for '%s' to %s"))
1680 % pKnownFile
->GetFileName() % GetClientFullInfo());
1683 AddDebugLogLineN(logAICHTransfer
,
1684 CFormat(wxT("AICH Packet Request: Failed to create recoverydata - Hashset not ready or requested Hash differs from Masterhash for '%s' to %s"))
1685 % pKnownFile
->GetFileName() % GetClientFullInfo());
1688 AddDebugLogLineN( logAICHTransfer
, wxT("AICH Packet Request: Failed to find requested shared file - ") + GetClientFullInfo() );
1691 CPacket
* packAnswer
= new CPacket(OP_AICHANSWER
, 16, OP_EMULEPROT
);
1692 packAnswer
->Copy16ToDataBuffer(hash
.GetHash());
1693 theStats::AddUpOverheadOther(packAnswer
->GetPacketSize());
1694 AddDebugLogLineN(logLocalClient
, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1695 SafeSendPacket(packAnswer
);
1698 void CUpDownClient::ProcessAICHFileHash(CMemFile
* data
, const CPartFile
* file
){
1699 CPartFile
* pPartFile
;
1701 pPartFile
= theApp
->downloadqueue
->GetFileByID(data
->ReadHash());
1703 pPartFile
= const_cast<CPartFile
*>(file
);
1705 CAICHHash
ahMasterHash(data
);
1707 if(pPartFile
!= NULL
&& pPartFile
== GetRequestFile()){
1708 SetReqFileAICHHash(new CAICHHash(ahMasterHash
));
1709 pPartFile
->GetAICHHashset()->UntrustedHashReceived(ahMasterHash
, GetConnectIP());
1711 AddDebugLogLineN( logAICHTransfer
, wxT("ProcessAICHFileHash(): PartFile not found or Partfile differs from requested file, ") + GetClientFullInfo() );
1714 // File_checked_for_headers