2 // This file is part of the aMule Project.
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 )
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" // Interface declarations
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;
407 m_nPartCount
= m_reqfile
->GetPartCount();
408 m_downPartStatus
.setsize( m_nPartCount
, 1);
410 m_bCompleteSource
= true;
414 // Somehow this happened.
416 throw strReqFileNull
;
418 if (m_reqfile
->GetED2KPartCount() != nED2KPartCount
)
421 strError
<< wxT("ProcessFileStatus - wrong part number recv=") << nED2KPartCount
<<
422 wxT(" expected=") << m_reqfile
->GetED2KPartCount() << wxT(" ") <<
423 m_reqfile
->GetFileHash().Encode();
427 m_nPartCount
= m_reqfile
->GetPartCount();
429 m_bCompleteSource
= false;
430 m_downPartStatus
.setsize( m_nPartCount
, 0 );
434 while (done
!= m_nPartCount
) {
435 uint8 toread
= data
->ReadUInt8();
437 for ( uint8 i
= 0;i
< 8; i
++ ) {
438 bool status
= ((toread
>>i
)&1)? 1:0;
439 m_downPartStatus
.set(done
, status
);
442 if (!m_reqfile
->IsComplete(done
)){
448 if (done
== m_nPartCount
) {
454 // We want the counts to be updated, even if we fail to read everything
455 m_reqfile
->UpdatePartsFrequency( this, true ); // Increment
461 m_reqfile
->UpdatePartsFrequency( this, true ); // Increment
463 UpdateDisplayedInfo();
465 // NOTE: This function is invoked from TCP and UDP socket!
468 SetDownloadState(DS_NONEEDEDPARTS
);
469 } else if (m_reqfile
->IsHashSetNeeded()) {
470 //If we are using the eMule filerequest packets, this is taken care of in the Multipacket!
472 CPacket
* packet
= new CPacket(OP_HASHSETREQUEST
,16, OP_EDONKEYPROT
);
473 packet
->Copy16ToDataBuffer((const char *)m_reqfile
->GetFileHash().GetHash());
474 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
475 AddDebugLogLineN(logLocalClient
, wxT("Local Client: OP_HASHSETREQUEST to ") + GetFullIP());
476 SendPacket(packet
, true, true);
477 SetDownloadState(DS_REQHASHSET
);
478 m_fHashsetRequesting
= 1;
479 m_reqfile
->SetHashSetNeeded(false);
485 SendStartupLoadReq();
490 SetDownloadState(DS_NONEEDEDPARTS
);
492 SetDownloadState(DS_ONQUEUE
);
495 m_reqfile
->UpdatePartsInfo();
498 bool CUpDownClient::AddRequestForAnotherFile(CPartFile
* file
)
500 if ( m_A4AF_list
.find( file
) == m_A4AF_list
.end() ) {
501 // When we access a non-existing entry entry, it will be zeroed by default,
502 // so we have to set NeededParts. All in one go.
503 m_A4AF_list
[file
].NeededParts
= true;
504 file
->AddA4AFSource( this );
511 bool CUpDownClient::DeleteFileRequest(CPartFile
* file
)
513 return (m_A4AF_list
.erase( file
) > 0);
516 void CUpDownClient::DeleteAllFileRequests()
522 /* eMule 0.30c implementation, i give it a try (Creteil) BEGIN ... */
523 void CUpDownClient::SetDownloadState(uint8 byNewState
)
525 if (m_nDownloadState
!= byNewState
) {
527 // Notify the client that this source has changed its state
528 m_reqfile
->ClientStateChanged( m_nDownloadState
, byNewState
);
530 if (byNewState
== DS_DOWNLOADING
) {
531 m_reqfile
->AddDownloadingSource(this);
532 } else if (m_nDownloadState
== DS_DOWNLOADING
) {
533 m_reqfile
->RemoveDownloadingSource(this);
536 if (byNewState
== DS_DOWNLOADING
) {
537 msReceivedPrev
= GetTickCount();
538 theStats::AddDownloadingSource();
539 } else if (m_nDownloadState
== DS_DOWNLOADING
) {
540 theStats::RemoveDownloadingSource();
543 if (m_nDownloadState
== DS_DOWNLOADING
) {
544 m_nDownloadState
= byNewState
;
545 ClearDownloadBlockRequests();
548 bytesReceivedCycle
= 0;
550 if (byNewState
== DS_NONE
) {
552 m_reqfile
->UpdatePartsFrequency( this, false ); // Decrement
554 m_downPartStatus
.clear();
557 if (m_socket
&& byNewState
!= DS_ERROR
) {
558 m_socket
->DisableDownloadLimit();
561 m_nDownloadState
= byNewState
;
562 if(GetDownloadState() == DS_DOWNLOADING
) {
563 if (IsEmuleClient()) {
564 SetRemoteQueueFull(false);
566 SetRemoteQueueRank(0); // eMule 0.30c set like this ...
568 UpdateDisplayedInfo(true);
571 /* eMule 0.30c implementation, i give it a try (Creteil) END ... */
573 void CUpDownClient::ProcessHashSet(const byte
* packet
, uint32 size
)
575 if ((!m_reqfile
) || md4cmp(packet
,m_reqfile
->GetFileHash().GetHash())) {
576 throw wxString(wxT("Wrong fileid sent (ProcessHashSet)"));
578 if (!m_fHashsetRequesting
) {
579 throw wxString(wxT("Received unsolicited hashset, ignoring it."));
581 CMemFile
data(packet
,size
);
582 if (m_reqfile
->LoadHashsetFromFile(&data
,true)) {
583 m_fHashsetRequesting
= 0;
585 m_reqfile
->SetHashSetNeeded(true);
586 throw wxString(wxT("Corrupted or invalid hashset received"));
588 SendStartupLoadReq();
591 void CUpDownClient::SendBlockRequests()
593 uint32 current_time
= ::GetTickCount();
596 // Ask new blocks only when all completed
597 if (m_PendingBlocks_list
.size()) {
601 if ((m_dwLastBlockReceived
+ SEC2MS(5)) > current_time
) {
602 // We received last block in less than 5 secs? Let's request faster.
603 m_MaxBlockRequests
= m_MaxBlockRequests
<< 1;
604 if ( m_MaxBlockRequests
> 0x20) {
605 m_MaxBlockRequests
= 0x20;
608 m_MaxBlockRequests
= m_MaxBlockRequests
>> 1;
609 if ( m_MaxBlockRequests
< STANDARD_BLOCKS_REQUEST
) {
610 m_MaxBlockRequests
= STANDARD_BLOCKS_REQUEST
;
615 m_dwLastBlockReceived
= current_time
;
621 uint8 version
= GetVBTTags() ? 2 : 1;
623 if (m_DownloadBlocks_list
.empty()) {
624 // Barry - instead of getting 3, just get how many is needed
625 uint16 count
= m_MaxBlockRequests
- m_PendingBlocks_list
.size();
626 std::vector
<Requested_Block_Struct
*> toadd
;
627 if (m_reqfile
->GetNextRequestedBlock(this, toadd
, count
)) {
628 for (int i
= 0; i
!= count
; i
++) {
629 m_DownloadBlocks_list
.push_back(toadd
[i
]);
634 // Barry - Why are unfinished blocks requested again, not just new ones?
636 while (m_PendingBlocks_list
.size() < m_MaxBlockRequests
&& !m_DownloadBlocks_list
.empty()) {
637 Pending_Block_Struct
* pblock
= new Pending_Block_Struct
;
638 pblock
->block
= m_DownloadBlocks_list
.front();
639 pblock
->zStream
= NULL
;
640 pblock
->totalUnzipped
= 0;
641 pblock
->fZStreamError
= 0;
642 pblock
->fRecovered
= 0;
643 m_PendingBlocks_list
.push_back(pblock
);
644 m_DownloadBlocks_list
.pop_front();
648 if (m_PendingBlocks_list
.empty()) {
650 CUpDownClient
* slower_client
= NULL
;
652 if (thePrefs::GetDropSlowSources()) {
653 slower_client
= m_reqfile
->GetSlowerDownloadingClient(m_lastaverage
, this);
656 if (slower_client
== NULL
) {
657 slower_client
= this;
660 if (!slower_client
->GetSentCancelTransfer()) {
661 CPacket
* packet
= new CPacket(OP_CANCELTRANSFER
, 0, OP_EDONKEYPROT
);
662 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
663 if (slower_client
!= this) {
664 // printf("Dropped client %p to allow client %p to download\n",slower_client, this);
666 slower_client
->ClearDownloadBlockRequests();
667 slower_client
->SendPacket(packet
,true,true);
668 slower_client
->SetSentCancelTransfer(1);
671 slower_client
->SetDownloadState(DS_NONEEDEDPARTS
);
673 if (slower_client
!= this) {
674 // Re-request freed blocks.
675 AddDebugLogLineN( logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER (faster source eager to transfer) to ") + slower_client
->GetFullIP() );
676 wxASSERT(m_DownloadBlocks_list
.empty());
677 wxASSERT(m_PendingBlocks_list
.empty());
678 uint16 count
= m_MaxBlockRequests
;
679 std::vector
<Requested_Block_Struct
*> toadd
;
680 if (m_reqfile
->GetNextRequestedBlock(this, toadd
, count
)) {
681 for (int i
= 0; i
!= count
; i
++) {
682 Pending_Block_Struct
* pblock
= new Pending_Block_Struct
;
683 pblock
->block
= toadd
[i
];
684 pblock
->zStream
= NULL
;
685 pblock
->totalUnzipped
= 0;
686 pblock
->fZStreamError
= 0;
687 pblock
->fRecovered
= 0;
688 m_PendingBlocks_list
.push_back(pblock
);
691 // WTF, we just freed blocks.
697 AddDebugLogLineN( logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER (no free blocks) to ") + GetFullIP() );
698 //#warning Kry - Would be nice to swap A4AF here.
703 CPacket
* packet
= NULL
;
708 // Most common scenario: hash + blocks to request + every one
709 // having 2 uint32 tags
711 uint8 nBlocks
= m_PendingBlocks_list
.size();
712 if (nBlocks
> m_MaxBlockRequests
) {
713 nBlocks
= m_MaxBlockRequests
;
716 CMemFile
data(16 + 1 + nBlocks
*((2+4)*2));
718 data
.WriteHash(m_reqfile
->GetFileHash());
720 data
.WriteUInt8(nBlocks
);
722 std::list
<Pending_Block_Struct
*>::iterator it
= m_PendingBlocks_list
.begin();
724 wxASSERT(it
!= m_PendingBlocks_list
.end());
725 wxASSERT( (*it
)->block
->StartOffset
<= (*it
)->block
->EndOffset
);
726 (*it
)->fZStreamError
= 0;
727 (*it
)->fRecovered
= 0;
728 CTagVarInt(/*Noname*/0,(*it
)->block
->StartOffset
).WriteTagToFile(&data
);
729 CTagVarInt(/*Noname*/0,(*it
)->block
->EndOffset
).WriteTagToFile(&data
);
734 packet
= new CPacket(data
, OP_ED2KV2HEADER
, OP_REQUESTPARTS
);
735 AddDebugLogLineN( logLocalClient
, CFormat(wxT("Local Client ED2Kv2: OP_REQUESTPARTS(%i) to %s"))
736 % (m_PendingBlocks_list
.size()<m_MaxBlockRequests
? m_PendingBlocks_list
.size() : m_MaxBlockRequests
) % GetFullIP() );
741 wxASSERT(m_MaxBlockRequests
== STANDARD_BLOCKS_REQUEST
);
743 //#warning Kry - I dont specially like this approach, we iterate one time too many
745 bool bHasLongBlocks
= false;
747 std::list
<Pending_Block_Struct
*>::iterator it
= m_PendingBlocks_list
.begin();
748 for (uint32 i
= 0; i
!= m_MaxBlockRequests
; i
++){
749 if (it
!= m_PendingBlocks_list
.end()) {
750 Pending_Block_Struct
* pending
= *it
++;
751 wxASSERT( pending
->block
->StartOffset
<= pending
->block
->EndOffset
);
752 if (pending
->block
->StartOffset
> 0xFFFFFFFF || pending
->block
->EndOffset
> 0xFFFFFFFF){
753 bHasLongBlocks
= true;
754 if (!SupportsLargeFiles()){
755 // Requesting a large block from a client that doesn't support large files?
757 if (!GetSentCancelTransfer()){
758 CPacket
* cancel_packet
= new CPacket(OP_CANCELTRANSFER
, 0, OP_EDONKEYPROT
);
759 theStats::AddUpOverheadFileRequest(cancel_packet
->GetPacketSize());
760 AddDebugLogLineN( logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
761 SendPacket(cancel_packet
,true,true);
762 SetSentCancelTransfer(1);
764 SetDownloadState(DS_ERROR
);
771 CMemFile
data(16 /*Hash*/ + (m_MaxBlockRequests
*(bHasLongBlocks
? 8 : 4) /* uint32/64 start*/) + (3*(bHasLongBlocks
? 8 : 4)/* uint32/64 end*/));
772 data
.WriteHash(m_reqfile
->GetFileHash());
774 it
= m_PendingBlocks_list
.begin();
775 for (uint32 i
= 0; i
!= m_MaxBlockRequests
; i
++) {
776 if (it
!= m_PendingBlocks_list
.end()) {
777 Pending_Block_Struct
* pending
= *it
++;
778 wxASSERT( pending
->block
->StartOffset
<= pending
->block
->EndOffset
);
779 pending
->fZStreamError
= 0;
780 pending
->fRecovered
= 0;
781 if (bHasLongBlocks
) {
782 data
.WriteUInt64(pending
->block
->StartOffset
);
784 data
.WriteUInt32(pending
->block
->StartOffset
);
787 if (bHasLongBlocks
) {
795 it
= m_PendingBlocks_list
.begin();
796 for (uint32 i
= 0; i
!= m_MaxBlockRequests
; i
++) {
797 if (it
!= m_PendingBlocks_list
.end()) {
798 Requested_Block_Struct
* block
= (*it
++)->block
;
799 if (bHasLongBlocks
) {
800 data
.WriteUInt64(block
->EndOffset
+1);
802 data
.WriteUInt32(block
->EndOffset
+1);
805 if (bHasLongBlocks
) {
812 packet
= new CPacket(data
, (bHasLongBlocks
? OP_EMULEPROT
: OP_EDONKEYPROT
), (bHasLongBlocks
? (uint8
)OP_REQUESTPARTS_I64
: (uint8
)OP_REQUESTPARTS
));
813 AddDebugLogLineN(logLocalClient
, CFormat(wxT("Local Client: %s to %s")) % (bHasLongBlocks
? wxT("OP_REQUESTPARTS_I64") : wxT("OP_REQUESTPARTS")) % GetFullIP());
821 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
822 SendPacket(packet
, true, true);
829 Barry - Originally this only wrote to disk when a full 180k block
830 had been received from a client, and only asked for data in
833 This meant that on average 90k was lost for every connection
834 to a client data source. That is a lot of wasted data.
836 To reduce the lost data, packets are now written to a buffer
837 and flushed to disk regularly regardless of size downloaded.
839 This includes compressed packets.
841 Data is also requested only where gaps are, not in 180k blocks.
842 The requests will still not exceed 180k, but may be smaller to
846 void CUpDownClient::ProcessBlockPacket(const byte
* packet
, uint32 size
, bool packed
, bool largeblocks
)
848 // Ignore if no data required
849 if (!(GetDownloadState() == DS_DOWNLOADING
|| GetDownloadState() == DS_NONEEDEDPARTS
)) {
853 // This vars are defined here to be able to use them on the catch
854 int header_size
= 16;
855 uint64 nStartPos
= 0;
857 uint32 nBlockSize
= 0;
858 uint32 lenUnzipped
= 0;
861 m_dwLastBlockReceived
= ::GetTickCount();
865 // Read data from packet
866 const CMemFile
data(packet
, size
);
868 // Check that this data is for the correct file
869 if ((!m_reqfile
) || data
.ReadHash() != m_reqfile
->GetFileHash()) {
870 throw wxString(wxT("Wrong fileid sent (ProcessBlockPacket)"));
873 // Find the start & end positions, and size of this chunk of data
876 nStartPos
= data
.ReadUInt64();
879 nStartPos
= data
.ReadUInt32();
884 nBlockSize
= data
.ReadUInt32();
886 nEndPos
= nStartPos
+ (size
- header_size
);
889 nEndPos
= data
.ReadUInt64();
892 nEndPos
= data
.ReadUInt32();
897 // Check that packet size matches the declared data size + header size
898 if ( nEndPos
== nStartPos
|| size
!= ((nEndPos
- nStartPos
) + header_size
)) {
899 throw wxString(wxT("Corrupted or invalid DataBlock received (ProcessBlockPacket)"));
901 theStats::AddDownloadFromSoft(GetClientSoft(),size
- header_size
);
902 bytesReceivedCycle
+= size
- header_size
;
904 credits
->AddDownloaded(size
- header_size
, GetIP(), theApp
->CryptoAvailable());
906 // Move end back one, should be inclusive
909 // Loop through to find the reserved block that this is within
910 std::list
<Pending_Block_Struct
*>::iterator it
= m_PendingBlocks_list
.begin();
911 for (; it
!= m_PendingBlocks_list
.end(); ++it
) {
912 Pending_Block_Struct
* cur_block
= *it
;
914 if ((cur_block
->block
->StartOffset
<= nStartPos
) && (cur_block
->block
->EndOffset
>= nStartPos
)) {
915 // Found reserved block
917 if (cur_block
->block
->StartOffset
== nStartPos
) {
918 // This block just started transfering. Set the start time.
919 m_last_block_start
= ::GetTickCountFullRes();
922 if (cur_block
->fZStreamError
){
923 AddDebugLogLineN(logZLib
,
924 CFormat(wxT("Ignoring %u bytes of block %u-%u because of erroneous zstream state for file: %s"))
925 % (size
- header_size
) % nStartPos
% nEndPos
% m_reqfile
->GetFileName());
926 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
930 // Remember this start pos, used to draw part downloading in list
931 m_lastDownloadingPart
= nStartPos
/ PARTSIZE
;
933 // Occasionally packets are duplicated, no point writing it twice
934 // This will be 0 in these cases, or the length written otherwise
935 uint32 lenWritten
= 0;
937 // Handle differently depending on whether packed or not
939 // security sanitize check
940 if (nEndPos
> cur_block
->block
->EndOffset
) {
941 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()));
942 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
945 // Write to disk (will be buffered in part file class)
946 lenWritten
= m_reqfile
->WriteToBuffer( size
- header_size
, (byte
*)(packet
+ header_size
), nStartPos
, nEndPos
, cur_block
->block
, this);
949 wxASSERT( (long int)size
> 0 );
950 // Create space to store unzipped data, the size is
951 // only an initial guess, will be resized in unzip()
953 lenUnzipped
= (size
* 2);
955 if (lenUnzipped
> (BLOCKSIZE
+ 300)) {
956 lenUnzipped
= (BLOCKSIZE
+ 300);
958 byte
*unzipped
= new byte
[lenUnzipped
];
960 // Try to unzip the packet
961 int result
= unzip(cur_block
, (byte
*)(packet
+ header_size
), (size
- header_size
), &unzipped
, &lenUnzipped
);
963 // no block can be uncompressed to >2GB, 'lenUnzipped' is obviously erroneous.
964 if (result
== Z_OK
&& ((int)lenUnzipped
>= 0)) {
966 // Write any unzipped data to disk
967 if (lenUnzipped
> 0) {
968 wxASSERT( (int)lenUnzipped
> 0 );
970 // Use the current start and end positions for the uncompressed data
971 nStartPos
= cur_block
->block
->StartOffset
+ cur_block
->totalUnzipped
- lenUnzipped
;
972 nEndPos
= cur_block
->block
->StartOffset
+ cur_block
->totalUnzipped
- 1;
974 if (nStartPos
> cur_block
->block
->EndOffset
|| nEndPos
> cur_block
->block
->EndOffset
) {
975 AddDebugLogLineN(logZLib
,
976 CFormat(wxT("Corrupted compressed packet for '%s' received (error 666)")) % m_reqfile
->GetFileName());
977 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
979 // Write uncompressed data to file
980 lenWritten
= m_reqfile
->WriteToBuffer( size
- header_size
,
989 wxString strZipError
;
990 if (cur_block
->zStream
&& cur_block
->zStream
->msg
) {
991 strZipError
= wxT(" - ") + wxString::FromAscii(cur_block
->zStream
->msg
);
994 AddDebugLogLineN(logZLib
,
995 CFormat(wxT("Corrupted compressed packet for '%s' received (error %i): %s"))
996 % m_reqfile
->GetFileName() % result
% strZipError
);
998 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
1000 // If we had an zstream error, there is no chance that we could recover from it nor that we
1001 // could use the current zstream (which is in error state) any longer.
1002 if (cur_block
->zStream
){
1003 inflateEnd(cur_block
->zStream
);
1004 delete cur_block
->zStream
;
1005 cur_block
->zStream
= NULL
;
1008 // Although we can't further use the current zstream, there is no need to disconnect the sending
1009 // client because the next zstream (a series of 10K-blocks which build a 180K-block) could be
1010 // valid again. Just ignore all further blocks for the current zstream.
1011 cur_block
->fZStreamError
= 1;
1012 cur_block
->totalUnzipped
= 0; // bluecow's fix
1016 // These checks only need to be done if any data was written
1017 if (lenWritten
> 0) {
1018 m_nTransferredDown
+= lenWritten
;
1020 // If finished reserved block
1021 if (nEndPos
== cur_block
->block
->EndOffset
) {
1023 // Save last average speed based on data and time.
1024 // This should do bytes/sec.
1025 uint32 average_time
= (::GetTickCountFullRes() - m_last_block_start
);
1027 // Avoid divide by 0.
1028 if (average_time
== 0) {
1032 m_lastaverage
= ((cur_block
->block
->EndOffset
- cur_block
->block
->StartOffset
) * 1000) / average_time
;
1034 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
1035 delete cur_block
->block
;
1036 // Not always allocated
1037 if (cur_block
->zStream
) {
1038 inflateEnd(cur_block
->zStream
);
1039 delete cur_block
->zStream
;
1042 m_PendingBlocks_list
.erase(it
);
1044 // Request next block
1045 SendBlockRequests();
1048 // Stop looping and exit method
1052 } catch (const CEOFException
& e
) {
1053 wxString error
= wxString(wxT("Error reading "));
1054 if (packed
) error
+= CFormat(wxT("packed (LU: %i) largeblocks ")) % lenUnzipped
;
1055 error
+= CFormat(wxT("data packet: RS: %i HS: %i SP: %i EP: %i BS: %i -> "))
1056 % size
% header_size
% nStartPos
% nEndPos
% nBlockSize
;
1057 AddDebugLogLineC(logRemoteClient
, error
+ e
.what());
1062 int CUpDownClient::unzip(Pending_Block_Struct
*block
, byte
*zipped
, uint32 lenZipped
, byte
**unzipped
, uint32
*lenUnzipped
, int iRecursion
)
1064 int err
= Z_DATA_ERROR
;
1067 z_stream
*zS
= block
->zStream
;
1069 // Is this the first time this block has been unzipped
1072 block
->zStream
= new z_stream
;
1073 zS
= block
->zStream
;
1075 // Initialise stream values
1076 zS
->zalloc
= (alloc_func
)0;
1077 zS
->zfree
= (free_func
)0;
1078 zS
->opaque
= (voidpf
)0;
1080 // Set output data streams, do this here to avoid overwriting on recursive calls
1081 zS
->next_out
= (*unzipped
);
1082 zS
->avail_out
= (*lenUnzipped
);
1084 // Initialise the z_stream
1085 err
= inflateInit(zS
);
1091 // Use whatever input is provided
1092 zS
->next_in
= zipped
;
1093 zS
->avail_in
= lenZipped
;
1095 // Only set the output if not being called recursively
1096 if (iRecursion
== 0) {
1097 zS
->next_out
= (*unzipped
);
1098 zS
->avail_out
= (*lenUnzipped
);
1101 // Try to unzip the data
1102 err
= inflate(zS
, Z_SYNC_FLUSH
);
1104 // Is zip finished reading all currently available input and writing
1105 // all generated output
1106 if (err
== Z_STREAM_END
) {
1108 err
= inflateEnd(zS
);
1113 // Got a good result, set the size to the amount unzipped in this call
1114 // (including all recursive calls)
1115 (*lenUnzipped
) = (zS
->total_out
- block
->totalUnzipped
);
1116 block
->totalUnzipped
= zS
->total_out
;
1117 } else if ((err
== Z_OK
) && (zS
->avail_out
== 0) && (zS
->avail_in
!= 0)) {
1119 // Output array was not big enough,
1120 // call recursively until there is enough space
1122 // What size should we try next
1123 uint32 newLength
= (*lenUnzipped
) *= 2;
1124 if (newLength
== 0) {
1125 newLength
= lenZipped
* 2;
1127 // Copy any data that was successfully unzipped to new array
1128 byte
*temp
= new byte
[newLength
];
1129 wxASSERT( zS
->total_out
- block
->totalUnzipped
<= newLength
);
1130 memcpy(temp
, (*unzipped
), (zS
->total_out
- block
->totalUnzipped
));
1131 delete [] (*unzipped
);
1133 (*lenUnzipped
) = newLength
;
1135 // Position stream output to correct place in new array
1136 zS
->next_out
= (*unzipped
) + (zS
->total_out
- block
->totalUnzipped
);
1137 zS
->avail_out
= (*lenUnzipped
) - (zS
->total_out
- block
->totalUnzipped
);
1140 err
= unzip(block
, zS
->next_in
, zS
->avail_in
, unzipped
, lenUnzipped
, iRecursion
+ 1);
1141 } else if ((err
== Z_OK
) && (zS
->avail_in
== 0)) {
1142 // All available input has been processed, everything ok.
1143 // Set the size to the amount unzipped in this call
1144 // (including all recursive calls)
1145 (*lenUnzipped
) = (zS
->total_out
- block
->totalUnzipped
);
1146 block
->totalUnzipped
= zS
->total_out
;
1148 // Should not get here unless input data is corrupt
1149 wxString strZipError
;
1152 strZipError
= CFormat(wxT(" %d '%s'")) % err
% wxString::FromAscii(zS
->msg
);
1153 } else if (err
!= Z_OK
) {
1154 strZipError
= CFormat(wxT(" %d")) % err
;
1157 AddDebugLogLineN(logZLib
,
1158 CFormat(wxT("Unexpected zip error %s in file '%s'"))
1159 % strZipError
% (m_reqfile
? m_reqfile
->GetFileName() : CPath(wxT("?"))));
1170 // Speed is now updated only when data was received, calculated as
1171 // (data received) / (time since last receiption)
1172 // and slightly filtered (10s average).
1173 // Result is quite precise now and makes the DownloadRateAdjust workaround obsolete.
1175 float CUpDownClient::CalculateKBpsDown()
1177 const float tAverage
= 10.0;
1178 uint32 msCur
= GetTickCount();
1180 if (bytesReceivedCycle
) {
1181 float dt
= (msCur
- msReceivedPrev
) / 1000.0; // time since last reception
1182 if (dt
< 0.01) { // (safeguard against divide-by-zero)
1183 dt
= 0.01f
; // diff should be 100ms actually
1185 float kBpsDownCur
= bytesReceivedCycle
/ 1024.0 / dt
;
1186 if (dt
>= tAverage
) {
1187 kBpsDown
= kBpsDownCur
;
1189 kBpsDown
= (kBpsDown
* (tAverage
- dt
) + kBpsDownCur
* dt
) / tAverage
;
1191 //AddDebugLogLineN(logLocalClient, CFormat(wxT("CalculateKBpsDown %p kbps %.1f kbpsCur %.1f dt %.3f rcv %d "))
1192 // % this % kBpsDown % kBpsDownCur % dt % bytesReceivedCycle);
1193 bytesReceivedCycle
= 0;
1194 msReceivedPrev
= msCur
;
1198 if (m_cShowDR
== 30){
1200 UpdateDisplayedInfo();
1202 if (msCur
- m_dwLastBlockReceived
> DOWNLOADTIMEOUT
) {
1203 if (!GetSentCancelTransfer()){
1204 CPacket
* packet
= new CPacket(OP_CANCELTRANSFER
, 0, OP_EDONKEYPROT
);
1205 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
1206 AddDebugLogLineN( logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
1207 SendPacket(packet
,true,true);
1208 SetSentCancelTransfer(1);
1210 SetDownloadState(DS_ONQUEUE
);
1216 uint16
CUpDownClient::GetAvailablePartCount() const
1219 for (int i
= 0;i
!= m_nPartCount
;i
++){
1220 if (IsPartAvailable(i
))
1226 void CUpDownClient::SetRemoteQueueRank(uint16 nr
)
1228 m_nOldRemoteQueueRank
= m_nRemoteQueueRank
;
1229 m_nRemoteQueueRank
= nr
;
1230 UpdateDisplayedInfo();
1233 void CUpDownClient::UDPReaskACK(uint16 nNewQR
)
1236 m_bUDPPending
= false;
1237 SetRemoteQueueRank(nNewQR
);
1238 m_dwLastAskedTime
= ::GetTickCount();
1241 void CUpDownClient::UDPReaskFNF()
1243 m_bUDPPending
= false;
1245 // avoid premature deletion of 'this' client
1246 if (GetDownloadState() != DS_DOWNLOADING
){
1248 m_reqfile
->AddDeadSource(this);
1251 theApp
->downloadqueue
->RemoveSource(this);
1253 if (Disconnected(wxT("UDPReaskFNF m_socket=NULL"))) {
1258 AddDebugLogLineN( logRemoteClient
, wxT("UDP ANSWER FNF : ") + GetUserName() + wxT(" - did not remove client because of current download state") );
1262 void CUpDownClient::UDPReaskForDownload()
1265 wxASSERT(m_reqfile
);
1267 if(!m_reqfile
|| m_bUDPPending
) {
1271 //#warning We should implement the quality tests for udp reliability
1273 if( m_nTotalUDPPackets > 3 && ((float)(m_nFailedUDPPackets/m_nTotalUDPPackets) > .3)) {
1278 if (thePrefs::GetEffectiveUDPPort() == 0) {
1282 if (m_nUDPPort
!= 0 && !theApp
->IsFirewalled() && !IsConnected()) {
1283 //don't use udp to ask for sources
1284 if(IsSourceRequestAllowed()) {
1288 m_bUDPPending
= true;
1291 data
.WriteHash(m_reqfile
->GetFileHash());
1293 if (GetUDPVersion() > 3) {
1294 if (m_reqfile
->IsPartFile()) {
1295 ((CPartFile
*)m_reqfile
)->WritePartStatus(&data
);
1298 data
.WriteUInt16(0);
1302 if (GetUDPVersion() > 2) {
1303 data
.WriteUInt16(m_reqfile
->m_nCompleteSourcesCount
);
1306 CPacket
* response
= new CPacket(data
, OP_EMULEPROT
, OP_REASKFILEPING
);
1307 AddDebugLogLineN( logClientUDP
, wxT("Client UDP socket: send OP_REASKFILEPING") );
1308 theStats::AddUpOverheadFileRequest(response
->GetPacketSize());
1309 theApp
->clientudp
->SendPacket(response
,GetConnectIP(),GetUDPPort(), ShouldReceiveCryptUDPPackets(), GetUserHash().GetHash(), false, 0);
1310 } else if (HasLowID() && GetBuddyIP() && GetBuddyPort() && HasValidBuddyID()) {
1312 m_bUDPPending
= true;
1316 data
.WriteHash(CMD4Hash(GetBuddyID()));
1317 data
.WriteHash(m_reqfile
->GetFileHash());
1319 if (GetUDPVersion() > 3) {
1320 if (m_reqfile
->IsPartFile()) {
1321 ((CPartFile
*)m_reqfile
)->WritePartStatus(&data
);
1323 data
.WriteUInt16(0);
1327 if (GetUDPVersion() > 2) {
1328 data
.WriteUInt16(m_reqfile
->m_nCompleteSourcesCount
);
1331 CPacket
* response
= new CPacket(data
, OP_EMULEPROT
, OP_REASKCALLBACKUDP
);
1332 AddDebugLogLineN( logClientUDP
, wxT("Client UDP socket: send OP_REASKCALLBACKUDP") );
1333 theStats::AddUpOverheadFileRequest(response
->GetPacketSize());
1334 theApp
->clientudp
->SendPacket(response
, GetBuddyIP(), GetBuddyPort(), false, NULL
, true, 0 );
1339 // Get the next part that is requested
1340 uint16
CUpDownClient::GetNextRequestedPart() const
1342 uint16 part
= 0xffff;
1344 std::list
<Pending_Block_Struct
*>::const_iterator it
= m_PendingBlocks_list
.begin();
1345 for (; it
!= m_PendingBlocks_list
.end(); ++it
) {
1346 part
= (*it
)->block
->StartOffset
/ PARTSIZE
;
1347 if (part
!= m_lastDownloadingPart
) {
1356 void CUpDownClient::UpdateDisplayedInfo(bool force
)
1358 uint32 curTick
= ::GetTickCount();
1359 if(force
|| curTick
-m_lastRefreshedDLDisplay
> MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE
) {
1360 // Check if we actually need to notify of changes
1361 bool update
= m_reqfile
&& m_reqfile
->ShowSources();
1363 // Check A4AF files only if needed
1365 A4AFList::iterator it
= m_A4AF_list
.begin();
1366 for ( ; it
!= m_A4AF_list
.end(); ++it
) {
1367 if ( it
->first
->ShowSources() ) {
1374 // And finnaly trigger an event if there's any reason
1376 SourceItemType type
= A4AF_SOURCE
;
1377 switch (GetDownloadState()) {
1378 case DS_DOWNLOADING
:
1380 // We will send A4AF, which will be checked.
1383 type
= UNAVAILABLE_SOURCE
;
1387 Notify_SourceCtrlUpdateSource(this, type
);
1388 Notify_SharedCtrlRefreshClient(this, AVAILABLE_SOURCE
);
1391 m_lastRefreshedDLDisplay
= curTick
;
1395 uint8
CUpDownClient::GetObfuscationStatus() const
1397 uint8 ret
= OBST_UNDEFINED
;
1398 if (thePrefs::IsClientCryptLayerSupported()) {
1399 if (SupportsCryptLayer()) {
1400 if ((RequestsCryptLayer() || thePrefs::IsClientCryptLayerRequested()) && HasObfuscatedConnectionBeenEstablished()) {
1403 ret
= OBST_SUPPORTED
;
1406 ret
= OBST_NOT_SUPPORTED
;
1409 ret
= OBST_DISABLED
;
1414 // IgnoreNoNeeded = will switch to files of which this source has no needed parts (if no better fiels found)
1415 // ignoreSuspensions = ignore timelimit for A4Af jumping
1416 // bRemoveCompletely = do not readd the file which the source is swapped from to the A4AF lists (needed if deleting or stopping a file)
1417 // toFile = Try to swap to this partfile only
1419 bool CUpDownClient::SwapToAnotherFile(bool bIgnoreNoNeeded
, bool ignoreSuspensions
, bool bRemoveCompletely
, CPartFile
* toFile
)
1421 // Fail if m_reqfile is invalid
1422 if ( m_reqfile
== NULL
) {
1426 // It would be stupid to swap away a downloading source
1427 if (GetDownloadState() == DS_DOWNLOADING
) {
1431 // The iterator of the final target
1432 A4AFList::iterator target
= m_A4AF_list
.end();
1434 // Do we want to swap to a specific file?
1435 if ( toFile
!= NULL
) {
1436 A4AFList::iterator it
= m_A4AF_list
.find( toFile
);
1437 if ( it
!= m_A4AF_list
.end() ) {
1439 // We force ignoring of timestamps
1440 if ( IsValidSwapTarget( it
, bIgnoreNoNeeded
, true ) ) {
1446 // We want highest priority possible, but need to start with
1447 // a value less than any other priority
1450 A4AFList::iterator it
= m_A4AF_list
.begin();
1451 for ( ; it
!= m_A4AF_list
.end(); ++it
) {
1452 if ( IsValidSwapTarget( it
, bIgnoreNoNeeded
, ignoreSuspensions
) ) {
1453 char cur_priority
= it
->first
->GetDownPriority();
1455 // We would prefer to get files with needed parts, thus rate them higher.
1456 // However, this really only matters if bIgnoreNoNeeded is true.
1457 if ( it
->second
.NeededParts
)
1460 // Change target if the current file has a higher rate than the previous
1461 if ( cur_priority
> priority
) {
1462 priority
= cur_priority
;
1464 // Set the new target
1467 // Break on the first High-priority file with needed parts
1468 if ( priority
== PR_HIGH
+ 10 ) {
1476 // Try to swap if we found a valid target
1477 if ( target
!= m_A4AF_list
.end() ) {
1479 // Sanity check, if reqfile doesn't own the source, then something
1480 // is wrong and the swap cannot proceed.
1481 if ( m_reqfile
->DelSource( this ) ) {
1482 CPartFile
* SwapTo
= target
->first
;
1484 // remove this client from the A4AF list of our new m_reqfile
1485 if ( SwapTo
->RemoveA4AFSource( this ) ) {
1486 Notify_SourceCtrlRemoveSource(this, SwapTo
);
1489 m_reqfile
->RemoveDownloadingSource( this );
1491 // Do we want to remove it completly? Say if the old file is getting deleted
1492 if ( !bRemoveCompletely
) {
1493 m_reqfile
->AddA4AFSource( this );
1495 // Set the status of the old file
1496 m_A4AF_list
[m_reqfile
].NeededParts
= (GetDownloadState() != DS_NONEEDEDPARTS
);
1498 // Avoid swapping to this file for a while
1499 m_A4AF_list
[m_reqfile
].timestamp
= ::GetTickCount();
1501 Notify_SourceCtrlAddSource(m_reqfile
, this, A4AF_SOURCE
);
1503 Notify_SourceCtrlRemoveSource( this, m_reqfile
);
1506 SetDownloadState(DS_NONE
);
1507 ResetFileStatusInfo();
1509 m_nRemoteQueueRank
= 0;
1510 m_nOldRemoteQueueRank
= 0;
1512 m_reqfile
->UpdatePartsInfo();
1514 SetRequestFile( SwapTo
);
1516 SwapTo
->AddSource( this );
1518 Notify_SourceCtrlAddSource(SwapTo
, this, UNAVAILABLE_SOURCE
);
1520 // Remove the new reqfile from the list of other files
1521 m_A4AF_list
.erase( target
);
1531 bool CUpDownClient::IsValidSwapTarget( A4AFList::iterator it
, bool ignorenoneeded
, bool ignoresuspended
)
1533 wxASSERT( it
!= m_A4AF_list
.end() && it
->first
);
1535 // Check if this file has been suspended
1536 if ( !ignoresuspended
) {
1537 if ( ::GetTickCount() - it
->second
.timestamp
>= PURGESOURCESWAPSTOP
) {
1538 // The wait-time has been exceeded and the file is now a valid target
1539 it
->second
.timestamp
= 0;
1541 // The file was still suspended and we are not ignoring suspensions
1546 // Check if the client has needed parts
1547 if ( !ignorenoneeded
) {
1548 if ( !it
->second
.NeededParts
) {
1553 // Final checks to see if the client is a valid target
1554 CPartFile
* cur_file
= it
->first
;
1555 if ( ( cur_file
!= m_reqfile
&& !cur_file
->IsStopped() ) &&
1556 ( cur_file
->GetStatus() == PS_READY
|| cur_file
->GetStatus() == PS_EMPTY
) &&
1557 ( cur_file
->IsPartFile() ) )
1566 void CUpDownClient::SetRequestFile(CPartFile
* reqfile
)
1568 if ( m_reqfile
!= reqfile
) {
1569 // Decrement the source-count of the old request-file
1571 m_reqfile
->ClientStateChanged( GetDownloadState(), -1 );
1572 m_reqfile
->UpdatePartsFrequency( this, false );
1576 m_downPartStatus
.clear();
1578 m_reqfile
= reqfile
;
1581 // Increment the source-count of the new request-file
1582 m_reqfile
->ClientStateChanged( -1, GetDownloadState() );
1584 m_nPartCount
= reqfile
->GetPartCount();
1589 void CUpDownClient::SetReqFileAICHHash(CAICHHash
* val
){
1590 if(m_pReqFileAICHHash
!= NULL
&& m_pReqFileAICHHash
!= val
)
1591 delete m_pReqFileAICHHash
;
1592 m_pReqFileAICHHash
= val
;
1595 void CUpDownClient::SendAICHRequest(CPartFile
* pForFile
, uint16 nPart
){
1596 CAICHRequestedData request
;
1597 request
.m_nPart
= nPart
;
1598 request
.m_pClient
= this;
1599 request
.m_pPartFile
= pForFile
;
1600 CAICHHashSet::m_liRequestedData
.push_back(request
);
1601 m_fAICHRequested
= TRUE
;
1603 data
.WriteHash(pForFile
->GetFileHash());
1604 data
.WriteUInt16(nPart
);
1605 pForFile
->GetAICHHashset()->GetMasterHash().Write(&data
);
1606 CPacket
* packet
= new CPacket(data
, OP_EMULEPROT
, OP_AICHREQUEST
);
1607 theStats::AddUpOverheadOther(packet
->GetPacketSize());
1608 AddDebugLogLineN(logLocalClient
, wxT("Local Client: OP_AICHREQUEST to") + GetFullIP());
1609 SafeSendPacket(packet
);
1612 void CUpDownClient::ProcessAICHAnswer(const byte
* packet
, uint32 size
)
1614 if (m_fAICHRequested
== FALSE
){
1615 throw wxString(wxT("Received unrequested AICH Packet"));
1617 m_fAICHRequested
= FALSE
;
1619 CMemFile
data(packet
, size
);
1621 CAICHHashSet::ClientAICHRequestFailed(this);
1625 CMD4Hash hash
= data
.ReadHash();
1626 CPartFile
* pPartFile
= theApp
->downloadqueue
->GetFileByID(hash
);
1627 CAICHRequestedData request
= CAICHHashSet::GetAICHReqDetails(this);
1628 uint16 nPart
= data
.ReadUInt16();
1629 if (pPartFile
!= NULL
&& request
.m_pPartFile
== pPartFile
&& request
.m_pClient
== this && nPart
== request
.m_nPart
){
1630 CAICHHash
ahMasterHash(&data
);
1631 if ( (pPartFile
->GetAICHHashset()->GetStatus() == AICH_TRUSTED
|| pPartFile
->GetAICHHashset()->GetStatus() == AICH_VERIFIED
)
1632 && ahMasterHash
== pPartFile
->GetAICHHashset()->GetMasterHash())
1634 if(pPartFile
->GetAICHHashset()->ReadRecoveryData(request
.m_nPart
*PARTSIZE
, &data
)){
1635 // finally all checks passed, everythings seem to be fine
1636 AddDebugLogLineN(logAICHTransfer
, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1637 CAICHHashSet::RemoveClientAICHRequest(this);
1638 pPartFile
->AICHRecoveryDataAvailable(request
.m_nPart
);
1641 AddDebugLogLineN(logAICHTransfer
, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1644 AddDebugLogLineN( logAICHTransfer
, wxT("AICH Packet Answer: Masterhash differs from packethash or hashset has no trusted Masterhash") );
1647 AddDebugLogLineN( logAICHTransfer
, wxT("AICH Packet Answer: requested values differ from values in packet") );
1650 CAICHHashSet::ClientAICHRequestFailed(this);
1654 void CUpDownClient::ProcessAICHRequest(const byte
* packet
, uint32 size
)
1656 if (size
!= 16 + 2 + CAICHHash::GetHashSize()) {
1657 throw wxString(wxT("Received AICH Request Packet with wrong size"));
1660 CMemFile
data(packet
, size
);
1662 CMD4Hash hash
= data
.ReadHash();
1663 uint16 nPart
= data
.ReadUInt16();
1664 CAICHHash
ahMasterHash(&data
);
1665 CKnownFile
* pKnownFile
= theApp
->sharedfiles
->GetFileByID(hash
);
1666 if (pKnownFile
!= NULL
){
1667 if (pKnownFile
->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE
&& pKnownFile
->GetAICHHashset()->HasValidMasterHash()
1668 && pKnownFile
->GetAICHHashset()->GetMasterHash() == ahMasterHash
&& pKnownFile
->GetPartCount() > nPart
1669 && pKnownFile
->GetFileSize() > EMBLOCKSIZE
&& pKnownFile
->GetFileSize() - PARTSIZE
*nPart
> EMBLOCKSIZE
)
1671 CMemFile fileResponse
;
1672 fileResponse
.WriteHash(pKnownFile
->GetFileHash());
1673 fileResponse
.WriteUInt16(nPart
);
1674 pKnownFile
->GetAICHHashset()->GetMasterHash().Write(&fileResponse
);
1675 if (pKnownFile
->GetAICHHashset()->CreatePartRecoveryData(nPart
*PARTSIZE
, &fileResponse
)){
1676 AddDebugLogLineN(logAICHTransfer
,
1677 CFormat(wxT("AICH Packet Request: Sucessfully created and send recoverydata for '%s' to %s"))
1678 % pKnownFile
->GetFileName() % GetClientFullInfo());
1680 CPacket
* packAnswer
= new CPacket(fileResponse
, OP_EMULEPROT
, OP_AICHANSWER
);
1681 theStats::AddUpOverheadOther(packAnswer
->GetPacketSize());
1682 AddDebugLogLineN(logLocalClient
, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1683 SafeSendPacket(packAnswer
);
1686 AddDebugLogLineN(logAICHTransfer
,
1687 CFormat(wxT("AICH Packet Request: Failed to create recoverydata for '%s' to %s"))
1688 % pKnownFile
->GetFileName() % GetClientFullInfo());
1691 AddDebugLogLineN(logAICHTransfer
,
1692 CFormat(wxT("AICH Packet Request: Failed to create recoverydata - Hashset not ready or requested Hash differs from Masterhash for '%s' to %s"))
1693 % pKnownFile
->GetFileName() % GetClientFullInfo());
1696 AddDebugLogLineN( logAICHTransfer
, wxT("AICH Packet Request: Failed to find requested shared file - ") + GetClientFullInfo() );
1699 CPacket
* packAnswer
= new CPacket(OP_AICHANSWER
, 16, OP_EMULEPROT
);
1700 packAnswer
->Copy16ToDataBuffer(hash
.GetHash());
1701 theStats::AddUpOverheadOther(packAnswer
->GetPacketSize());
1702 AddDebugLogLineN(logLocalClient
, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1703 SafeSendPacket(packAnswer
);
1706 void CUpDownClient::ProcessAICHFileHash(CMemFile
* data
, const CPartFile
* file
){
1707 CPartFile
* pPartFile
;
1709 pPartFile
= theApp
->downloadqueue
->GetFileByID(data
->ReadHash());
1711 pPartFile
= (CPartFile
*)file
;
1713 CAICHHash
ahMasterHash(data
);
1715 if(pPartFile
!= NULL
&& pPartFile
== GetRequestFile()){
1716 SetReqFileAICHHash(new CAICHHash(ahMasterHash
));
1717 pPartFile
->GetAICHHashset()->UntrustedHashReceived(ahMasterHash
, GetConnectIP());
1719 AddDebugLogLineN( logAICHTransfer
, wxT("ProcessAICHFileHash(): PartFile not found or Partfile differs from requested file, ") + GetClientFullInfo() );
1722 // File_checked_for_headers