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_*
54 #ifdef __MULE_UNUSED_CODE__
55 // This function is left as a reminder.
56 // Changes here _must_ be reflected in CClientList::FindMatchingClient.
57 bool CUpDownClient::Compare(const CUpDownClient
* tocomp
, bool bIgnoreUserhash
) const
60 // should we wxASSERT here?
64 //Compare only the user hash..
65 if(!bIgnoreUserhash
&& HasValidHash() && tocomp
->HasValidHash()) {
66 return GetUserHash() == tocomp
->GetUserHash();
70 //User is firewalled.. Must do two checks..
71 if (GetIP()!=0 && GetIP() == tocomp
->GetIP()) {
72 //The IP of both match
73 if (GetUserPort()!=0 && GetUserPort() == tocomp
->GetUserPort()) {
77 if (GetKadPort()!=0 && GetKadPort() == tocomp
->GetKadPort()) {
83 if (GetUserIDHybrid()!=0
84 && GetUserIDHybrid() == tocomp
->GetUserIDHybrid()
86 && GetServerIP() == tocomp
->GetServerIP()
88 && GetServerPort() == tocomp
->GetServerPort()) {
89 //Both have the same lowID, Same serverIP and Port..
93 //Both IP, and Server do not match..
97 //User is not firewalled.
98 if (GetUserPort()!=0) {
99 //User has a Port, lets check the rest.
100 if (GetIP() != 0 && tocomp
->GetIP() != 0) {
101 //Both clients have a verified IP..
102 if(GetIP() == tocomp
->GetIP() && GetUserPort() == tocomp
->GetUserPort()) {
103 //IP and UserPort match..
107 //One of the two clients do not have a verified IP
108 if (GetUserIDHybrid() == tocomp
->GetUserIDHybrid() && GetUserPort() == tocomp
->GetUserPort()) {
109 //ID and Port Match..
115 if(GetKadPort()!=0) {
116 //User has a Kad Port.
117 if(GetIP() != 0 && tocomp
->GetIP() != 0) {
118 //Both clients have a verified IP.
119 if(GetIP() == tocomp
->GetIP() && GetKadPort() == tocomp
->GetKadPort()) {
120 //IP and KadPort Match..
124 //One of the users do not have a verified IP.
125 if (GetUserIDHybrid() == tocomp
->GetUserIDHybrid() && GetKadPort() == tocomp
->GetKadPort()) {
126 //ID and KadProt Match..
138 bool CUpDownClient::AskForDownload()
141 if (theApp
->listensocket
->TooManySockets()) {
143 if (GetDownloadState() != DS_TOOMANYCONNS
) {
144 SetDownloadState(DS_TOOMANYCONNS
);
147 } else if (!m_socket
->IsConnected()) {
148 if (GetDownloadState() != DS_TOOMANYCONNS
) {
149 SetDownloadState(DS_TOOMANYCONNS
);
154 m_bUDPPending
= false;
155 m_dwLastAskedTime
= ::GetTickCount();
156 SetDownloadState(DS_CONNECTING
);
157 return TryToConnect();
161 void CUpDownClient::SendStartupLoadReq()
164 if (m_socket
==NULL
|| m_reqfile
==NULL
) {
167 SetDownloadState(DS_ONQUEUE
);
168 CMemFile
dataStartupLoadReq(16);
169 dataStartupLoadReq
.WriteHash(m_reqfile
->GetFileHash());
170 CPacket
* packet
= new CPacket(dataStartupLoadReq
, OP_EDONKEYPROT
, OP_STARTUPLOADREQ
);
171 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
172 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_STARTUPLOADREQ to ") + GetFullIP());
173 SendPacket(packet
, true, true);
177 bool CUpDownClient::IsSourceRequestAllowed()
179 //#warning REWRITE - Source swapping from eMule.
181 uint32 dwTickCount
= ::GetTickCount() + CONNECTION_LATENCY
;
182 uint32 nTimePassedClient
= dwTickCount
- GetLastSrcAnswerTime();
183 uint32 nTimePassedFile
= dwTickCount
- m_reqfile
->GetLastAnsweredTime();
184 bool bNeverAskedBefore
= (GetLastAskedForSources() == 0);
186 uint32 uSources
= m_reqfile
->GetSourceCount();
188 // if client has the correct extended protocol
189 ExtProtocolAvailable() && (SupportsSourceExchange2() || GetSourceExchange1Version() > 1) &&
190 // AND if we need more sources
191 thePrefs::GetMaxSourcePerFileSoft() > uSources
&&
194 //source is not complete and file is very rare
196 && (bNeverAskedBefore
|| nTimePassedClient
> SOURCECLIENTREASKS
)
197 && (uSources
<= RARE_FILE
/5)
199 //source is not complete and file is rare
201 && (bNeverAskedBefore
|| nTimePassedClient
> SOURCECLIENTREASKS
)
202 && (uSources
<= RARE_FILE
|| uSources
- m_reqfile
->GetValidSourcesCount() <= RARE_FILE
/ 2)
203 && (nTimePassedFile
> SOURCECLIENTREASKF
)
205 // OR if file is not rare
206 ( (bNeverAskedBefore
|| nTimePassedClient
> (unsigned)(SOURCECLIENTREASKS
* MINCOMMONPENALTY
))
207 && (nTimePassedFile
> (unsigned)(SOURCECLIENTREASKF
* MINCOMMONPENALTY
))
214 void CUpDownClient::SendFileRequest()
216 wxCHECK_RET(m_reqfile
, wxT("Cannot request file when no reqfile is set"));
218 CMemFile
dataFileReq(16+16);
219 dataFileReq
.WriteHash(m_reqfile
->GetFileHash());
221 if (SupportMultiPacket()) {
222 wxString sent_opcodes
;
224 if (SupportExtMultiPacket()) {
225 dataFileReq
.WriteUInt64(m_reqfile
->GetFileSize());
228 AddDebugLogLineM(false, logClient
, wxT("Sending file request to client"));
230 dataFileReq
.WriteUInt8(OP_REQUESTFILENAME
);
231 sent_opcodes
+= wxT("|RFNM|");
232 // Extended information
233 if (GetExtendedRequestsVersion() > 0) {
234 m_reqfile
->WritePartStatus(&dataFileReq
);
236 if (GetExtendedRequestsVersion() > 1) {
237 m_reqfile
->WriteCompleteSourcesCount(&dataFileReq
);
239 if (m_reqfile
->GetPartCount() > 1) {
240 sent_opcodes
+= wxT("|RFID|");
241 dataFileReq
.WriteUInt8(OP_SETREQFILEID
);
243 if (IsEmuleClient()) {
244 SetRemoteQueueFull( true );
245 SetRemoteQueueRank(0);
247 if (IsSourceRequestAllowed()) {
248 if (SupportsSourceExchange2()){
249 sent_opcodes
+= wxT("|RSRC2|");
250 dataFileReq
.WriteUInt8(OP_REQUESTSOURCES2
);
251 dataFileReq
.WriteUInt8(SOURCEEXCHANGE2_VERSION
);
252 const uint16 nOptions
= 0; // 16 ... Reserved
253 dataFileReq
.WriteUInt16(nOptions
);
255 sent_opcodes
+= wxT("|RSRC|");
256 dataFileReq
.WriteUInt8(OP_REQUESTSOURCES
);
258 m_reqfile
->SetLastAnsweredTimeTimeout();
259 SetLastAskedForSources();
261 if (IsSupportingAICH()) {
262 sent_opcodes
+= wxT("|AFHR|");
263 dataFileReq
.WriteUInt8(OP_AICHFILEHASHREQ
);
265 CPacket
* packet
= new CPacket(dataFileReq
, OP_EMULEPROT
, (SupportExtMultiPacket() ? OP_MULTIPACKET_EXT
: OP_MULTIPACKET
));
266 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
267 AddDebugLogLineM( false, logLocalClient
, wxString::Format(wxT("Local Client: %s "), (SupportExtMultiPacket() ? wxT("OP_MULTIPACKET_EXT (") : wxT("OP_MULTIPACKET (") )) + sent_opcodes
+ wxT(") to ") + GetFullIP());
268 SendPacket(packet
, true);
270 //This is extended information
271 if (GetExtendedRequestsVersion() > 0 ) {
272 m_reqfile
->WritePartStatus(&dataFileReq
);
274 if (GetExtendedRequestsVersion() > 1 ) {
275 m_reqfile
->WriteCompleteSourcesCount(&dataFileReq
);
277 CPacket
* packet
= new CPacket(dataFileReq
, OP_EDONKEYPROT
, OP_REQUESTFILENAME
);
278 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
279 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_REQUESTFILENAME to ") + GetFullIP() );
280 SendPacket(packet
, true);
282 // 26-Jul-2003: removed requesting the file status for files <= PARTSIZE for better compatibility with ed2k protocol (eDonkeyHybrid).
283 // if the remote client answers the OP_REQUESTFILENAME with OP_REQFILENAMEANSWER the file is shared by the remote client. if we
284 // know that the file is shared, we know also that the file is complete and don't need to request the file status.
286 // Sending the packet could have deleted the client, check m_reqfile
287 if (m_reqfile
&& (m_reqfile
->GetPartCount() > 1)) {
288 CMemFile
dataSetReqFileID(16);
289 dataSetReqFileID
.WriteHash(m_reqfile
->GetFileHash());
290 packet
= new CPacket(dataSetReqFileID
, OP_EDONKEYPROT
, OP_SETREQFILEID
);
291 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
292 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_SETREQFILEID to ") + GetFullIP());
293 SendPacket(packet
, true);
296 if (IsEmuleClient()) {
297 SetRemoteQueueFull( true );
298 SetRemoteQueueRank(0);
301 // Sending the packet could have deleted the client, check m_reqfile
302 if (m_reqfile
&& IsSourceRequestAllowed()) {
303 m_reqfile
->SetLastAnsweredTimeTimeout();
307 if (SupportsSourceExchange2()) {
308 packetdata
.WriteUInt8(SOURCEEXCHANGE2_VERSION
);
309 packetdata
.WriteUInt16(0 /* Reserved */);
312 packetdata
.WriteHash(m_reqfile
->GetFileHash());
314 packet
= new CPacket(packetdata
, OP_EMULEPROT
, SupportsSourceExchange2() ? OP_REQUESTSOURCES2
: OP_REQUESTSOURCES
);
316 theStats::AddUpOverheadSourceExchange(packet
->GetPacketSize());
317 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_REQUESTSOURCES to ") + GetFullIP() );
318 SendPacket(packet
,true,true);
319 SetLastAskedForSources();
322 // Sending the packet could have deleted the client, check m_reqfile
323 if (m_reqfile
&& IsSupportingAICH()) {
324 packet
= new CPacket(OP_AICHFILEHASHREQ
,16,OP_EMULEPROT
);
325 packet
->Copy16ToDataBuffer((const char *)m_reqfile
->GetFileHash().GetHash());
326 theStats::AddUpOverheadOther(packet
->GetPacketSize());
327 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_AICHFILEHASHREQ to ") + GetFullIP());
328 SendPacket(packet
,true,true);
334 void CUpDownClient::ProcessFileInfo(const CMemFile
* data
, const CPartFile
* file
)
338 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; file==NULL)"));
340 if (m_reqfile
==NULL
) {
341 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; m_reqfile==NULL)"));
343 if (file
!= m_reqfile
) {
344 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; m_reqfile!=file)"));
347 m_clientFilename
= data
->ReadString((GetUnicodeSupport() != utf8strNone
));
349 // 26-Jul-2003: removed requesting the file status for files <= PARTSIZE for better compatibility with ed2k protocol (eDonkeyHybrid).
350 // if the remote client answers the OP_REQUESTFILENAME with OP_REQFILENAMEANSWER the file is shared by the remote client. if we
351 // know that the file is shared, we know also that the file is complete and don't need to request the file status.
352 if (m_reqfile
->GetPartCount() == 1) {
353 m_nPartCount
= m_reqfile
->GetPartCount();
355 m_reqfile
->UpdatePartsFrequency( this, false ); // Decrement
356 m_downPartStatus
.clear();
357 m_downPartStatus
.resize( m_nPartCount
, 1 );
358 m_reqfile
->UpdatePartsFrequency( this, true ); // Increment
360 m_bCompleteSource
= true;
362 UpdateDisplayedInfo();
363 // even if the file is <= PARTSIZE, we _may_ need the hashset for that file (if the file size == PARTSIZE)
364 if (m_reqfile
->IsHashSetNeeded()) {
366 CPacket
* packet
= new CPacket(OP_HASHSETREQUEST
,16, OP_EDONKEYPROT
);
367 packet
->Copy16ToDataBuffer((const char *)m_reqfile
->GetFileHash().GetHash());
368 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
369 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_HASHSETREQUEST to ") + GetFullIP());
370 SendPacket(packet
,true,true);
371 SetDownloadState(DS_REQHASHSET
);
372 m_fHashsetRequesting
= 1;
373 m_reqfile
->SetHashSetNeeded(false);
378 SendStartupLoadReq();
380 m_reqfile
->UpdatePartsInfo();
384 void CUpDownClient::ProcessFileStatus(bool bUdpPacket
, const CMemFile
* data
, const CPartFile
* file
)
387 wxString
strReqFileNull(wxT("ERROR: Wrong file ID (ProcessFileStatus; m_reqfile==NULL)"));
389 if ( !m_reqfile
|| file
!= m_reqfile
){
391 throw strReqFileNull
;
393 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileStatus; m_reqfile!=file)"));
396 uint16 nED2KPartCount
= data
->ReadUInt16();
398 m_reqfile
->UpdatePartsFrequency( this, false ); // Decrement
399 m_downPartStatus
.clear();
401 bool bPartsNeeded
= false;
405 m_nPartCount
= m_reqfile
->GetPartCount();
406 m_downPartStatus
.resize( m_nPartCount
, 1);
408 m_bCompleteSource
= true;
412 // Somehow this happened.
414 throw strReqFileNull
;
416 if (m_reqfile
->GetED2KPartCount() != nED2KPartCount
)
419 strError
<< wxT("ProcessFileStatus - wrong part number recv=") << nED2KPartCount
<<
420 wxT(" expected=") << m_reqfile
->GetED2KPartCount() << wxT(" ") <<
421 m_reqfile
->GetFileHash().Encode();
425 m_nPartCount
= m_reqfile
->GetPartCount();
427 m_bCompleteSource
= false;
428 m_downPartStatus
.resize( m_nPartCount
, 0 );
432 while (done
!= m_nPartCount
) {
433 uint8 toread
= data
->ReadUInt8();
435 for ( uint8 i
= 0;i
< 8; i
++ ) {
436 m_downPartStatus
[done
] = ((toread
>>i
)&1)? 1:0;
438 if ( m_downPartStatus
[done
] ) {
439 if (!m_reqfile
->IsComplete(done
)){
445 if (done
== m_nPartCount
) {
451 // We want the counts to be updated, even if we fail to read everything
452 m_reqfile
->UpdatePartsFrequency( this, true ); // Increment
458 m_reqfile
->UpdatePartsFrequency( this, true ); // Increment
460 UpdateDisplayedInfo();
462 // NOTE: This function is invoked from TCP and UDP socket!
465 SetDownloadState(DS_NONEEDEDPARTS
);
466 } else if (m_reqfile
->IsHashSetNeeded()) {
467 //If we are using the eMule filerequest packets, this is taken care of in the Multipacket!
469 CPacket
* packet
= new CPacket(OP_HASHSETREQUEST
,16, OP_EDONKEYPROT
);
470 packet
->Copy16ToDataBuffer((const char *)m_reqfile
->GetFileHash().GetHash());
471 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
472 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_HASHSETREQUEST to ") + GetFullIP());
473 SendPacket(packet
, true, true);
474 SetDownloadState(DS_REQHASHSET
);
475 m_fHashsetRequesting
= 1;
476 m_reqfile
->SetHashSetNeeded(false);
482 SendStartupLoadReq();
487 SetDownloadState(DS_NONEEDEDPARTS
);
489 SetDownloadState(DS_ONQUEUE
);
492 m_reqfile
->UpdatePartsInfo();
495 bool CUpDownClient::AddRequestForAnotherFile(CPartFile
* file
)
497 if ( m_A4AF_list
.find( file
) == m_A4AF_list
.end() ) {
498 // When we access a non-existing entry entry, it will be zeroed by default,
499 // so we have to set NeededParts. All in one go.
500 m_A4AF_list
[file
].NeededParts
= true;
501 file
->AddA4AFSource( this );
508 bool CUpDownClient::DeleteFileRequest(CPartFile
* file
)
510 return (m_A4AF_list
.erase( file
) > 0);
513 void CUpDownClient::DeleteAllFileRequests()
519 /* eMule 0.30c implementation, i give it a try (Creteil) BEGIN ... */
520 void CUpDownClient::SetDownloadState(uint8 byNewState
)
522 if (m_nDownloadState
!= byNewState
) {
524 // Notify the client that this source has changed its state
525 m_reqfile
->ClientStateChanged( m_nDownloadState
, byNewState
);
527 if (byNewState
== DS_DOWNLOADING
) {
528 m_reqfile
->AddDownloadingSource(this);
529 } else if (m_nDownloadState
== DS_DOWNLOADING
) {
530 m_reqfile
->RemoveDownloadingSource(this);
533 if (byNewState
== DS_DOWNLOADING
) {
534 msReceivedPrev
= GetTickCount();
535 theStats::AddDownloadingSource();
536 } else if (m_nDownloadState
== DS_DOWNLOADING
) {
537 theStats::RemoveDownloadingSource();
540 if (m_nDownloadState
== DS_DOWNLOADING
) {
541 m_nDownloadState
= byNewState
;
542 ClearDownloadBlockRequests();
545 bytesReceivedCycle
= 0;
547 if (byNewState
== DS_NONE
) {
549 m_reqfile
->UpdatePartsFrequency( this, false ); // Decrement
551 m_downPartStatus
.clear();
554 if (m_socket
&& byNewState
!= DS_ERROR
) {
555 m_socket
->DisableDownloadLimit();
558 m_nDownloadState
= byNewState
;
559 if(GetDownloadState() == DS_DOWNLOADING
) {
560 if (IsEmuleClient()) {
561 SetRemoteQueueFull(false);
563 SetRemoteQueueRank(0); // eMule 0.30c set like this ...
565 UpdateDisplayedInfo(true);
568 /* eMule 0.30c implementation, i give it a try (Creteil) END ... */
570 void CUpDownClient::ProcessHashSet(const byte
* packet
, uint32 size
)
572 if ((!m_reqfile
) || md4cmp(packet
,m_reqfile
->GetFileHash().GetHash())) {
573 throw wxString(wxT("Wrong fileid sent (ProcessHashSet)"));
575 if (!m_fHashsetRequesting
) {
576 throw wxString(wxT("Received unsolicited hashset, ignoring it."));
578 CMemFile
data(packet
,size
);
579 if (m_reqfile
->LoadHashsetFromFile(&data
,true)) {
580 m_fHashsetRequesting
= 0;
582 m_reqfile
->SetHashSetNeeded(true);
583 throw wxString(wxT("Corrupted or invalid hashset received"));
585 SendStartupLoadReq();
588 void CUpDownClient::SendBlockRequests()
590 uint32 current_time
= ::GetTickCount();
593 // Ask new blocks only when all completed
594 if (m_PendingBlocks_list
.size()) {
598 if ((m_dwLastBlockReceived
+ SEC2MS(5)) > current_time
) {
599 // We received last block in less than 5 secs? Let's request faster.
600 m_MaxBlockRequests
= m_MaxBlockRequests
<< 1;
601 if ( m_MaxBlockRequests
> 0x20) {
602 m_MaxBlockRequests
= 0x20;
605 m_MaxBlockRequests
= m_MaxBlockRequests
>> 1;
606 if ( m_MaxBlockRequests
< STANDARD_BLOCKS_REQUEST
) {
607 m_MaxBlockRequests
= STANDARD_BLOCKS_REQUEST
;
612 m_dwLastBlockReceived
= current_time
;
618 uint8 version
= GetVBTTags() ? 2 : 1;
620 if (m_DownloadBlocks_list
.empty()) {
621 // Barry - instead of getting 3, just get how many is needed
622 uint16 count
= m_MaxBlockRequests
- m_PendingBlocks_list
.size();
623 std::vector
<Requested_Block_Struct
*> toadd
;
624 if (m_reqfile
->GetNextRequestedBlock(this, toadd
, count
)) {
625 for (int i
= 0; i
!= count
; i
++) {
626 m_DownloadBlocks_list
.push_back(toadd
[i
]);
631 // Barry - Why are unfinished blocks requested again, not just new ones?
633 while (m_PendingBlocks_list
.size() < m_MaxBlockRequests
&& !m_DownloadBlocks_list
.empty()) {
634 Pending_Block_Struct
* pblock
= new Pending_Block_Struct
;
635 pblock
->block
= m_DownloadBlocks_list
.front();
636 pblock
->zStream
= NULL
;
637 pblock
->totalUnzipped
= 0;
638 pblock
->fZStreamError
= 0;
639 pblock
->fRecovered
= 0;
640 m_PendingBlocks_list
.push_back(pblock
);
641 m_DownloadBlocks_list
.pop_front();
645 if (m_PendingBlocks_list
.empty()) {
647 CUpDownClient
* slower_client
= NULL
;
649 if (thePrefs::GetDropSlowSources()) {
650 slower_client
= m_reqfile
->GetSlowerDownloadingClient(m_lastaverage
, this);
653 if (slower_client
== NULL
) {
654 slower_client
= this;
657 if (!slower_client
->GetSentCancelTransfer()) {
658 CPacket
* packet
= new CPacket(OP_CANCELTRANSFER
, 0, OP_EDONKEYPROT
);
659 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
660 if (slower_client
!= this) {
661 // printf("Dropped client %p to allow client %p to download\n",slower_client, this);
663 slower_client
->ClearDownloadBlockRequests();
664 slower_client
->SendPacket(packet
,true,true);
665 slower_client
->SetSentCancelTransfer(1);
668 slower_client
->SetDownloadState(DS_NONEEDEDPARTS
);
670 if (slower_client
!= this) {
671 // Re-request freed blocks.
672 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER (faster source eager to transfer) to ") + slower_client
->GetFullIP() );
673 wxASSERT(m_DownloadBlocks_list
.empty());
674 wxASSERT(m_PendingBlocks_list
.empty());
675 uint16 count
= m_MaxBlockRequests
;
676 std::vector
<Requested_Block_Struct
*> toadd
;
677 if (m_reqfile
->GetNextRequestedBlock(this, toadd
, count
)) {
678 for (int i
= 0; i
!= count
; i
++) {
679 Pending_Block_Struct
* pblock
= new Pending_Block_Struct
;
680 pblock
->block
= toadd
[i
];
681 pblock
->zStream
= NULL
;
682 pblock
->totalUnzipped
= 0;
683 pblock
->fZStreamError
= 0;
684 pblock
->fRecovered
= 0;
685 m_PendingBlocks_list
.push_back(pblock
);
688 // WTF, we just freed blocks.
694 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER (no free blocks) to ") + GetFullIP() );
695 //#warning Kry - Would be nice to swap A4AF here.
700 CPacket
* packet
= NULL
;
705 // Most common scenario: hash + blocks to request + every one
706 // having 2 uint32 tags
708 uint8 nBlocks
= m_PendingBlocks_list
.size();
709 if (nBlocks
> m_MaxBlockRequests
) {
710 nBlocks
= m_MaxBlockRequests
;
713 CMemFile
data(16 + 1 + nBlocks
*((2+4)*2));
715 data
.WriteHash(m_reqfile
->GetFileHash());
717 data
.WriteUInt8(nBlocks
);
719 std::list
<Pending_Block_Struct
*>::iterator it
= m_PendingBlocks_list
.begin();
721 wxASSERT(it
!= m_PendingBlocks_list
.end());
722 wxASSERT( (*it
)->block
->StartOffset
<= (*it
)->block
->EndOffset
);
723 (*it
)->fZStreamError
= 0;
724 (*it
)->fRecovered
= 0;
725 CTagVarInt(/*Noname*/0,(*it
)->block
->StartOffset
).WriteTagToFile(&data
);
726 CTagVarInt(/*Noname*/0,(*it
)->block
->EndOffset
).WriteTagToFile(&data
);
731 packet
= new CPacket(data
, OP_ED2KV2HEADER
, OP_REQUESTPARTS
);
732 AddDebugLogLineM( false, logLocalClient
, wxString::Format(wxT("Local Client ED2Kv2: OP_REQUESTPARTS(%i) to "),(m_PendingBlocks_list
.size()<m_MaxBlockRequests
) ? m_PendingBlocks_list
.size() : m_MaxBlockRequests
) + GetFullIP() );
737 wxASSERT(m_MaxBlockRequests
== STANDARD_BLOCKS_REQUEST
);
739 //#warning Kry - I dont specially like this approach, we iterate one time too many
741 bool bHasLongBlocks
= false;
743 std::list
<Pending_Block_Struct
*>::iterator it
= m_PendingBlocks_list
.begin();
744 for (uint32 i
= 0; i
!= m_MaxBlockRequests
; i
++){
745 if (it
!= m_PendingBlocks_list
.end()) {
746 Pending_Block_Struct
* pending
= *it
++;
747 wxASSERT( pending
->block
->StartOffset
<= pending
->block
->EndOffset
);
748 if (pending
->block
->StartOffset
> 0xFFFFFFFF || pending
->block
->EndOffset
> 0xFFFFFFFF){
749 bHasLongBlocks
= true;
750 if (!SupportsLargeFiles()){
751 // Requesting a large block from a client that doesn't support large files?
753 if (!GetSentCancelTransfer()){
754 CPacket
* cancel_packet
= new CPacket(OP_CANCELTRANSFER
, 0, OP_EDONKEYPROT
);
755 theStats::AddUpOverheadFileRequest(cancel_packet
->GetPacketSize());
756 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
757 SendPacket(cancel_packet
,true,true);
758 SetSentCancelTransfer(1);
760 SetDownloadState(DS_ERROR
);
767 CMemFile
data(16 /*Hash*/ + (m_MaxBlockRequests
*(bHasLongBlocks
? 8 : 4) /* uint32/64 start*/) + (3*(bHasLongBlocks
? 8 : 4)/* uint32/64 end*/));
768 data
.WriteHash(m_reqfile
->GetFileHash());
770 it
= m_PendingBlocks_list
.begin();
771 for (uint32 i
= 0; i
!= m_MaxBlockRequests
; i
++) {
772 if (it
!= m_PendingBlocks_list
.end()) {
773 Pending_Block_Struct
* pending
= *it
++;
774 wxASSERT( pending
->block
->StartOffset
<= pending
->block
->EndOffset
);
775 pending
->fZStreamError
= 0;
776 pending
->fRecovered
= 0;
777 if (bHasLongBlocks
) {
778 data
.WriteUInt64(pending
->block
->StartOffset
);
780 data
.WriteUInt32(pending
->block
->StartOffset
);
783 if (bHasLongBlocks
) {
791 it
= m_PendingBlocks_list
.begin();
792 for (uint32 i
= 0; i
!= m_MaxBlockRequests
; i
++) {
793 if (it
!= m_PendingBlocks_list
.end()) {
794 Requested_Block_Struct
* block
= (*it
++)->block
;
795 if (bHasLongBlocks
) {
796 data
.WriteUInt64(block
->EndOffset
+1);
798 data
.WriteUInt32(block
->EndOffset
+1);
801 if (bHasLongBlocks
) {
808 packet
= new CPacket(data
, (bHasLongBlocks
? OP_EMULEPROT
: OP_EDONKEYPROT
), (bHasLongBlocks
? (uint8
)OP_REQUESTPARTS_I64
: (uint8
)OP_REQUESTPARTS
));
809 AddDebugLogLineM( false, logLocalClient
, wxString::Format(wxT("Local Client: %s to "),(bHasLongBlocks
? wxT("OP_REQUESTPARTS_I64") : wxT("OP_REQUESTPARTS"))) + GetFullIP() );
817 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
818 SendPacket(packet
, true, true);
825 Barry - Originally this only wrote to disk when a full 180k block
826 had been received from a client, and only asked for data in
829 This meant that on average 90k was lost for every connection
830 to a client data source. That is a lot of wasted data.
832 To reduce the lost data, packets are now written to a buffer
833 and flushed to disk regularly regardless of size downloaded.
835 This includes compressed packets.
837 Data is also requested only where gaps are, not in 180k blocks.
838 The requests will still not exceed 180k, but may be smaller to
842 void CUpDownClient::ProcessBlockPacket(const byte
* packet
, uint32 size
, bool packed
, bool largeblocks
)
844 // Ignore if no data required
845 if (!(GetDownloadState() == DS_DOWNLOADING
|| GetDownloadState() == DS_NONEEDEDPARTS
)) {
849 // This vars are defined here to be able to use them on the catch
850 int header_size
= 16;
851 uint64 nStartPos
= 0;
853 uint32 nBlockSize
= 0;
854 uint32 lenUnzipped
= 0;
857 m_dwLastBlockReceived
= ::GetTickCount();
861 // Read data from packet
862 const CMemFile
data(packet
, size
);
864 // Check that this data is for the correct file
865 if ((!m_reqfile
) || data
.ReadHash() != m_reqfile
->GetFileHash()) {
866 throw wxString(wxT("Wrong fileid sent (ProcessBlockPacket)"));
869 // Find the start & end positions, and size of this chunk of data
872 nStartPos
= data
.ReadUInt64();
875 nStartPos
= data
.ReadUInt32();
880 nBlockSize
= data
.ReadUInt32();
882 nEndPos
= nStartPos
+ (size
- header_size
);
885 nEndPos
= data
.ReadUInt64();
888 nEndPos
= data
.ReadUInt32();
893 // Check that packet size matches the declared data size + header size
894 if ( nEndPos
== nStartPos
|| size
!= ((nEndPos
- nStartPos
) + header_size
)) {
895 throw wxString(wxT("Corrupted or invalid DataBlock received (ProcessBlockPacket)"));
897 theStats::AddDownloadFromSoft(GetClientSoft(),size
- header_size
);
898 bytesReceivedCycle
+= size
- header_size
;
900 credits
->AddDownloaded(size
- header_size
, GetIP(), theApp
->CryptoAvailable());
902 // Move end back one, should be inclusive
905 // Loop through to find the reserved block that this is within
906 std::list
<Pending_Block_Struct
*>::iterator it
= m_PendingBlocks_list
.begin();
907 for (; it
!= m_PendingBlocks_list
.end(); ++it
) {
908 Pending_Block_Struct
* cur_block
= *it
;
910 if ((cur_block
->block
->StartOffset
<= nStartPos
) && (cur_block
->block
->EndOffset
>= nStartPos
)) {
911 // Found reserved block
913 if (cur_block
->block
->StartOffset
== nStartPos
) {
914 // This block just started transfering. Set the start time.
915 m_last_block_start
= ::GetTickCountFullRes();
918 if (cur_block
->fZStreamError
){
919 AddDebugLogLineM( false, logZLib
,
920 CFormat(wxT("Ignoring %u bytes of block %u-%u because of erroneous zstream state for file: %s"))
921 % (size
- header_size
) % nStartPos
% nEndPos
% m_reqfile
->GetFileName());
922 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
926 // Remember this start pos, used to draw part downloading in list
927 m_nLastBlockOffset
= nStartPos
;
929 // Occasionally packets are duplicated, no point writing it twice
930 // This will be 0 in these cases, or the length written otherwise
931 uint32 lenWritten
= 0;
933 // Handle differently depending on whether packed or not
935 // security sanitize check
936 if (nEndPos
> cur_block
->block
->EndOffset
) {
937 AddDebugLogLineM(false, 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()));
938 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
941 // Write to disk (will be buffered in part file class)
942 lenWritten
= m_reqfile
->WriteToBuffer( size
- header_size
, (byte
*)(packet
+ header_size
), nStartPos
, nEndPos
, cur_block
->block
, this);
945 wxASSERT( (long int)size
> 0 );
946 // Create space to store unzipped data, the size is
947 // only an initial guess, will be resized in unzip()
949 lenUnzipped
= (size
* 2);
951 if (lenUnzipped
> (BLOCKSIZE
+ 300)) {
952 lenUnzipped
= (BLOCKSIZE
+ 300);
954 byte
*unzipped
= new byte
[lenUnzipped
];
956 // Try to unzip the packet
957 int result
= unzip(cur_block
, (byte
*)(packet
+ header_size
), (size
- header_size
), &unzipped
, &lenUnzipped
);
959 // no block can be uncompressed to >2GB, 'lenUnzipped' is obviously erroneous.
960 if (result
== Z_OK
&& ((int)lenUnzipped
>= 0)) {
962 // Write any unzipped data to disk
963 if (lenUnzipped
> 0) {
964 wxASSERT( (int)lenUnzipped
> 0 );
966 // Use the current start and end positions for the uncompressed data
967 nStartPos
= cur_block
->block
->StartOffset
+ cur_block
->totalUnzipped
- lenUnzipped
;
968 nEndPos
= cur_block
->block
->StartOffset
+ cur_block
->totalUnzipped
- 1;
970 if (nStartPos
> cur_block
->block
->EndOffset
|| nEndPos
> cur_block
->block
->EndOffset
) {
971 AddDebugLogLineM( false, logZLib
,
972 CFormat(wxT("Corrupted compressed packet for '%s' received (error 666)")) % m_reqfile
->GetFileName());
973 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
975 // Write uncompressed data to file
976 lenWritten
= m_reqfile
->WriteToBuffer( size
- header_size
,
985 wxString strZipError
;
986 if (cur_block
->zStream
&& cur_block
->zStream
->msg
) {
987 strZipError
= wxT(" - ") + wxString::FromAscii(cur_block
->zStream
->msg
);
990 AddDebugLogLineM( false, logZLib
,
991 CFormat(wxT("Corrupted compressed packet for '%s' received (error %i): %s"))
992 % m_reqfile
->GetFileName() % result
% strZipError
);
994 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
996 // If we had an zstream error, there is no chance that we could recover from it nor that we
997 // could use the current zstream (which is in error state) any longer.
998 if (cur_block
->zStream
){
999 inflateEnd(cur_block
->zStream
);
1000 delete cur_block
->zStream
;
1001 cur_block
->zStream
= NULL
;
1004 // Although we can't further use the current zstream, there is no need to disconnect the sending
1005 // client because the next zstream (a series of 10K-blocks which build a 180K-block) could be
1006 // valid again. Just ignore all further blocks for the current zstream.
1007 cur_block
->fZStreamError
= 1;
1008 cur_block
->totalUnzipped
= 0; // bluecow's fix
1012 // These checks only need to be done if any data was written
1013 if (lenWritten
> 0) {
1014 m_nTransferredDown
+= lenWritten
;
1016 // If finished reserved block
1017 if (nEndPos
== cur_block
->block
->EndOffset
) {
1019 // Save last average speed based on data and time.
1020 // This should do bytes/sec.
1021 uint32 average_time
= (::GetTickCountFullRes() - m_last_block_start
);
1023 // Avoid divide by 0.
1024 if (average_time
== 0) {
1028 m_lastaverage
= ((cur_block
->block
->EndOffset
- cur_block
->block
->StartOffset
) * 1000) / average_time
;
1030 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
1031 delete cur_block
->block
;
1032 // Not always allocated
1033 if (cur_block
->zStream
) {
1034 inflateEnd(cur_block
->zStream
);
1035 delete cur_block
->zStream
;
1038 m_PendingBlocks_list
.erase(it
);
1040 // Request next block
1041 SendBlockRequests();
1044 // Stop looping and exit method
1048 } catch (const CEOFException
& e
) {
1049 wxString error
= wxString(wxT("Error reading "));
1050 if (packed
) error
+= wxString::Format(wxT("packed (LU: %i) "),lenUnzipped
);
1051 if (packed
) error
+= wxT("largeblocks ");
1052 error
+= wxString::Format(wxT("data packet: RS: %i HS: %i SP: %i EP: %i BS: %i -> "),size
,header_size
,nStartPos
,nEndPos
,nBlockSize
);
1053 AddDebugLogLineM(true, logRemoteClient
, error
+ e
.what());
1058 int CUpDownClient::unzip(Pending_Block_Struct
*block
, byte
*zipped
, uint32 lenZipped
, byte
**unzipped
, uint32
*lenUnzipped
, int iRecursion
)
1060 int err
= Z_DATA_ERROR
;
1063 z_stream
*zS
= block
->zStream
;
1065 // Is this the first time this block has been unzipped
1068 block
->zStream
= new z_stream
;
1069 zS
= block
->zStream
;
1071 // Initialise stream values
1072 zS
->zalloc
= (alloc_func
)0;
1073 zS
->zfree
= (free_func
)0;
1074 zS
->opaque
= (voidpf
)0;
1076 // Set output data streams, do this here to avoid overwriting on recursive calls
1077 zS
->next_out
= (*unzipped
);
1078 zS
->avail_out
= (*lenUnzipped
);
1080 // Initialise the z_stream
1081 err
= inflateInit(zS
);
1087 // Use whatever input is provided
1088 zS
->next_in
= zipped
;
1089 zS
->avail_in
= lenZipped
;
1091 // Only set the output if not being called recursively
1092 if (iRecursion
== 0) {
1093 zS
->next_out
= (*unzipped
);
1094 zS
->avail_out
= (*lenUnzipped
);
1097 // Try to unzip the data
1098 err
= inflate(zS
, Z_SYNC_FLUSH
);
1100 // Is zip finished reading all currently available input and writing
1101 // all generated output
1102 if (err
== Z_STREAM_END
) {
1104 err
= inflateEnd(zS
);
1109 // Got a good result, set the size to the amount unzipped in this call
1110 // (including all recursive calls)
1111 (*lenUnzipped
) = (zS
->total_out
- block
->totalUnzipped
);
1112 block
->totalUnzipped
= zS
->total_out
;
1113 } else if ((err
== Z_OK
) && (zS
->avail_out
== 0) && (zS
->avail_in
!= 0)) {
1115 // Output array was not big enough,
1116 // call recursively until there is enough space
1118 // What size should we try next
1119 uint32 newLength
= (*lenUnzipped
) *= 2;
1120 if (newLength
== 0) {
1121 newLength
= lenZipped
* 2;
1123 // Copy any data that was successfully unzipped to new array
1124 byte
*temp
= new byte
[newLength
];
1125 wxASSERT( zS
->total_out
- block
->totalUnzipped
<= newLength
);
1126 memcpy(temp
, (*unzipped
), (zS
->total_out
- block
->totalUnzipped
));
1127 delete [] (*unzipped
);
1129 (*lenUnzipped
) = newLength
;
1131 // Position stream output to correct place in new array
1132 zS
->next_out
= (*unzipped
) + (zS
->total_out
- block
->totalUnzipped
);
1133 zS
->avail_out
= (*lenUnzipped
) - (zS
->total_out
- block
->totalUnzipped
);
1136 err
= unzip(block
, zS
->next_in
, zS
->avail_in
, unzipped
, lenUnzipped
, iRecursion
+ 1);
1137 } else if ((err
== Z_OK
) && (zS
->avail_in
== 0)) {
1138 // All available input has been processed, everything ok.
1139 // Set the size to the amount unzipped in this call
1140 // (including all recursive calls)
1141 (*lenUnzipped
) = (zS
->total_out
- block
->totalUnzipped
);
1142 block
->totalUnzipped
= zS
->total_out
;
1144 // Should not get here unless input data is corrupt
1145 wxString strZipError
;
1148 strZipError
= wxString::Format(wxT(" %d '"), err
) + wxString::FromAscii(zS
->msg
) + wxT("'");
1149 } else if (err
!= Z_OK
) {
1150 strZipError
= wxString::Format(wxT(" %d"), err
);
1153 AddDebugLogLineM(false, logZLib
,
1154 CFormat(wxT("Unexpected zip error %s in file '%s'"))
1155 % strZipError
% (m_reqfile
? m_reqfile
->GetFileName() : CPath(wxT("?"))));
1166 // Speed is now updated only when data was received, calculated as
1167 // (data received) / (time since last receiption)
1168 // and slightly filtered (10s average).
1169 // Result is quite precise now and makes the DownloadRateAdjust workaround obsolete.
1171 float CUpDownClient::CalculateKBpsDown()
1173 const float tAverage
= 10.0;
1174 uint32 msCur
= GetTickCount();
1176 if (bytesReceivedCycle
) {
1177 float dt
= (msCur
- msReceivedPrev
) / 1000.0; // time since last reception
1178 if (dt
< 0.01) { // (safeguard against divide-by-zero)
1179 dt
= 0.01f
; // diff should be 100ms actually
1181 float kBpsDownCur
= bytesReceivedCycle
/ 1024.0 / dt
;
1182 if (dt
>= tAverage
) {
1183 kBpsDown
= kBpsDownCur
;
1185 kBpsDown
= (kBpsDown
* (tAverage
- dt
) + kBpsDownCur
* dt
) / tAverage
;
1187 AddDebugLogLineM( false, logLocalClient
, CFormat(wxT("CalculateKBpsDown %p kbps %.1f kbpsCur %.1f dt %.3f rcv %d "))
1188 % this % kBpsDown
% kBpsDownCur
% dt
% bytesReceivedCycle
);
1189 bytesReceivedCycle
= 0;
1190 msReceivedPrev
= msCur
;
1194 if (m_cShowDR
== 30){
1196 UpdateDisplayedInfo();
1198 if (msCur
- m_dwLastBlockReceived
> DOWNLOADTIMEOUT
) {
1199 if (!GetSentCancelTransfer()){
1200 CPacket
* packet
= new CPacket(OP_CANCELTRANSFER
, 0, OP_EDONKEYPROT
);
1201 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
1202 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
1203 SendPacket(packet
,true,true);
1204 SetSentCancelTransfer(1);
1206 SetDownloadState(DS_ONQUEUE
);
1212 uint16
CUpDownClient::GetAvailablePartCount() const
1215 for (int i
= 0;i
!= m_nPartCount
;i
++){
1216 if (IsPartAvailable(i
))
1222 void CUpDownClient::SetRemoteQueueRank(uint16 nr
)
1224 m_nOldRemoteQueueRank
= m_nRemoteQueueRank
;
1225 m_nRemoteQueueRank
= nr
;
1226 UpdateDisplayedInfo();
1229 void CUpDownClient::UDPReaskACK(uint16 nNewQR
)
1232 m_bUDPPending
= false;
1233 SetRemoteQueueRank(nNewQR
);
1234 m_dwLastAskedTime
= ::GetTickCount();
1237 void CUpDownClient::UDPReaskFNF()
1239 m_bUDPPending
= false;
1241 // avoid premature deletion of 'this' client
1242 if (GetDownloadState() != DS_DOWNLOADING
){
1244 m_reqfile
->AddDeadSource(this);
1247 theApp
->downloadqueue
->RemoveSource(this);
1249 if (Disconnected(wxT("UDPReaskFNF m_socket=NULL"))) {
1254 AddDebugLogLineM( false, logRemoteClient
, wxT("UDP ANSWER FNF : ") + GetUserName() + wxT(" - did not remove client because of current download state") );
1258 void CUpDownClient::UDPReaskForDownload()
1261 wxASSERT(m_reqfile
);
1263 if(!m_reqfile
|| m_bUDPPending
) {
1267 //#warning We should implement the quality tests for udp reliability
1269 if( m_nTotalUDPPackets > 3 && ((float)(m_nFailedUDPPackets/m_nTotalUDPPackets) > .3)) {
1274 if (thePrefs::GetEffectiveUDPPort() == 0) {
1278 if (m_nUDPPort
!= 0 && !theApp
->IsFirewalled() && !IsConnected()) {
1279 //don't use udp to ask for sources
1280 if(IsSourceRequestAllowed()) {
1284 m_bUDPPending
= true;
1287 data
.WriteHash(m_reqfile
->GetFileHash());
1289 if (GetUDPVersion() > 3) {
1290 if (m_reqfile
->IsPartFile()) {
1291 ((CPartFile
*)m_reqfile
)->WritePartStatus(&data
);
1294 data
.WriteUInt16(0);
1298 if (GetUDPVersion() > 2) {
1299 data
.WriteUInt16(m_reqfile
->m_nCompleteSourcesCount
);
1302 CPacket
* response
= new CPacket(data
, OP_EMULEPROT
, OP_REASKFILEPING
);
1303 AddDebugLogLineM( false, logClientUDP
, wxT("Client UDP socket: send OP_REASKFILEPING") );
1304 theStats::AddUpOverheadFileRequest(response
->GetPacketSize());
1305 theApp
->clientudp
->SendPacket(response
,GetConnectIP(),GetUDPPort(), ShouldReceiveCryptUDPPackets(), GetUserHash().GetHash(), false, 0);
1306 } else if (HasLowID() && GetBuddyIP() && GetBuddyPort() && HasValidBuddyID()) {
1308 m_bUDPPending
= true;
1312 data
.WriteHash(CMD4Hash(GetBuddyID()));
1313 data
.WriteHash(m_reqfile
->GetFileHash());
1315 if (GetUDPVersion() > 3) {
1316 if (m_reqfile
->IsPartFile()) {
1317 ((CPartFile
*)m_reqfile
)->WritePartStatus(&data
);
1319 data
.WriteUInt16(0);
1323 if (GetUDPVersion() > 2) {
1324 data
.WriteUInt16(m_reqfile
->m_nCompleteSourcesCount
);
1327 CPacket
* response
= new CPacket(data
, OP_EMULEPROT
, OP_REASKCALLBACKUDP
);
1328 AddDebugLogLineM( false, logClientUDP
, wxT("Client UDP socket: send OP_REASKCALLBACKUDP") );
1329 theStats::AddUpOverheadFileRequest(response
->GetPacketSize());
1330 theApp
->clientudp
->SendPacket(response
, GetBuddyIP(), GetBuddyPort(), false, NULL
, true, 0 );
1335 //! Barry - Sets string to show parts downloading, eg NNNYNNNNYYNYN
1336 wxString
CUpDownClient::ShowDownloadingParts() const
1338 // Initialise to all N's
1339 wxString
Parts(wxT('N'), m_nPartCount
);
1341 std::list
<Pending_Block_Struct
*>::const_iterator it
= m_PendingBlocks_list
.begin();
1342 for (; it
!= m_PendingBlocks_list
.end(); ++it
) {
1343 Parts
.SetChar(((*it
)->block
->StartOffset
/ PARTSIZE
), 'Y');
1350 void CUpDownClient::UpdateDisplayedInfo(bool force
)
1352 uint32 curTick
= ::GetTickCount();
1353 if(force
|| curTick
-m_lastRefreshedDLDisplay
> MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE
) {
1354 // Check if we actually need to notify of changes
1355 bool update
= m_reqfile
&& m_reqfile
->ShowSources();
1357 // Check A4AF files only if needed
1359 A4AFList::iterator it
= m_A4AF_list
.begin();
1360 for ( ; it
!= m_A4AF_list
.end(); ++it
) {
1361 if ( it
->first
->ShowSources() ) {
1368 // And finnaly trigger an event if there's any reason
1370 Notify_DownloadCtrlUpdateItem(this);
1373 m_lastRefreshedDLDisplay
= curTick
;
1378 // IgnoreNoNeeded = will switch to files of which this source has no needed parts (if no better fiels found)
1379 // ignoreSuspensions = ignore timelimit for A4Af jumping
1380 // bRemoveCompletely = do not readd the file which the source is swapped from to the A4AF lists (needed if deleting or stopping a file)
1381 // toFile = Try to swap to this partfile only
1383 bool CUpDownClient::SwapToAnotherFile(bool bIgnoreNoNeeded
, bool ignoreSuspensions
, bool bRemoveCompletely
, CPartFile
* toFile
)
1385 // Fail if m_reqfile is invalid
1386 if ( m_reqfile
== NULL
) {
1390 // It would be stupid to swap away a downloading source
1391 if (GetDownloadState() == DS_DOWNLOADING
) {
1395 // The iterator of the final target
1396 A4AFList::iterator target
= m_A4AF_list
.end();
1398 // Do we want to swap to a specific file?
1399 if ( toFile
!= NULL
) {
1400 A4AFList::iterator it
= m_A4AF_list
.find( toFile
);
1401 if ( it
!= m_A4AF_list
.end() ) {
1403 // We force ignoring of noneeded flag and timestamps
1404 if ( IsValidSwapTarget( it
, true, true ) ) {
1410 // We want highest priority possible, but need to start with
1411 // a value less than any other priority
1414 A4AFList::iterator it
= m_A4AF_list
.begin();
1415 for ( ; it
!= m_A4AF_list
.end(); ++it
) {
1416 if ( IsValidSwapTarget( it
, bIgnoreNoNeeded
, ignoreSuspensions
) ) {
1417 char cur_priority
= it
->first
->GetDownPriority();
1419 // We would prefer to get files with needed parts, thus rate them higher.
1420 // However, this really only matters if bIgnoreNoNeeded is true.
1421 if ( it
->second
.NeededParts
)
1424 // Change target if the current file has a higher rate than the previous
1425 if ( cur_priority
> priority
) {
1426 priority
= cur_priority
;
1428 // Set the new target
1431 // Break on the first High-priority file with needed parts
1432 if ( priority
== PR_HIGH
+ 10 ) {
1440 // Try to swap if we found a valid target
1441 if ( target
!= m_A4AF_list
.end() ) {
1443 // Sainity check, if reqfile doesn't own the source, then something
1444 // is wrong and the swap cannot proceed.
1445 if ( m_reqfile
->DelSource( this ) ) {
1446 CPartFile
* SwapTo
= target
->first
;
1448 // remove this client from the A4AF list of our new m_reqfile
1449 if ( SwapTo
->RemoveA4AFSource( this ) ) {
1450 Notify_DownloadCtrlRemoveSource(this, SwapTo
);
1453 m_reqfile
->RemoveDownloadingSource( this );
1455 // Do we want to remove it completly? Say if the old file is getting deleted
1456 if ( !bRemoveCompletely
) {
1457 m_reqfile
->AddA4AFSource( this );
1459 // Set the status of the old file
1460 m_A4AF_list
[m_reqfile
].NeededParts
= (GetDownloadState() != DS_NONEEDEDPARTS
);
1462 // Avoid swapping to this file for a while
1463 m_A4AF_list
[m_reqfile
].timestamp
= ::GetTickCount();
1465 Notify_DownloadCtrlAddSource(m_reqfile
, this, A4AF_SOURCE
);
1467 Notify_DownloadCtrlRemoveSource( this, m_reqfile
);
1470 SetDownloadState(DS_NONE
);
1471 ResetFileStatusInfo();
1473 m_nRemoteQueueRank
= 0;
1474 m_nOldRemoteQueueRank
= 0;
1476 m_reqfile
->UpdatePartsInfo();
1478 SetRequestFile( SwapTo
);
1480 SwapTo
->AddSource( this );
1482 Notify_DownloadCtrlAddSource(SwapTo
, this, UNAVAILABLE_SOURCE
);
1484 // Remove the new reqfile from the list of other files
1485 m_A4AF_list
.erase( target
);
1495 bool CUpDownClient::IsValidSwapTarget( A4AFList::iterator it
, bool ignorenoneeded
, bool ignoresuspended
)
1497 wxASSERT( it
!= m_A4AF_list
.end() && it
->first
);
1499 // Check if this file has been suspended
1500 if ( !ignoresuspended
) {
1501 if ( ::GetTickCount() - it
->second
.timestamp
>= PURGESOURCESWAPSTOP
) {
1502 // The wait-time has been exceeded and the file is now a valid target
1503 it
->second
.timestamp
= 0;
1505 // The file was still suspended and we are not ignoring suspensions
1510 // Check if the client has needed parts
1511 if ( !ignorenoneeded
) {
1512 if ( !it
->second
.NeededParts
) {
1517 // Final checks to see if the client is a valid target
1518 CPartFile
* cur_file
= it
->first
;
1519 if ( ( cur_file
!= m_reqfile
&& !cur_file
->IsStopped() ) &&
1520 ( cur_file
->GetStatus() == PS_READY
|| cur_file
->GetStatus() == PS_EMPTY
) &&
1521 ( cur_file
->IsPartFile() ) )
1530 void CUpDownClient::SetRequestFile(CPartFile
* reqfile
)
1532 if ( m_reqfile
!= reqfile
) {
1533 // Decrement the source-count of the old request-file
1535 m_reqfile
->ClientStateChanged( GetDownloadState(), -1 );
1536 m_reqfile
->UpdatePartsFrequency( this, false );
1540 m_downPartStatus
.clear();
1542 m_reqfile
= reqfile
;
1545 // Increment the source-count of the new request-file
1546 m_reqfile
->ClientStateChanged( -1, GetDownloadState() );
1548 m_nPartCount
= reqfile
->GetPartCount();
1553 void CUpDownClient::SetReqFileAICHHash(CAICHHash
* val
){
1554 if(m_pReqFileAICHHash
!= NULL
&& m_pReqFileAICHHash
!= val
)
1555 delete m_pReqFileAICHHash
;
1556 m_pReqFileAICHHash
= val
;
1559 void CUpDownClient::SendAICHRequest(CPartFile
* pForFile
, uint16 nPart
){
1560 CAICHRequestedData request
;
1561 request
.m_nPart
= nPart
;
1562 request
.m_pClient
= this;
1563 request
.m_pPartFile
= pForFile
;
1564 CAICHHashSet::m_liRequestedData
.push_back(request
);
1565 m_fAICHRequested
= TRUE
;
1567 data
.WriteHash(pForFile
->GetFileHash());
1568 data
.WriteUInt16(nPart
);
1569 pForFile
->GetAICHHashset()->GetMasterHash().Write(&data
);
1570 CPacket
* packet
= new CPacket(data
, OP_EMULEPROT
, OP_AICHREQUEST
);
1571 theStats::AddUpOverheadOther(packet
->GetPacketSize());
1572 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_AICHREQUEST to") + GetFullIP());
1573 SafeSendPacket(packet
);
1576 void CUpDownClient::ProcessAICHAnswer(const byte
* packet
, uint32 size
)
1578 if (m_fAICHRequested
== FALSE
){
1579 throw wxString(wxT("Received unrequested AICH Packet"));
1581 m_fAICHRequested
= FALSE
;
1583 CMemFile
data(packet
, size
);
1585 CAICHHashSet::ClientAICHRequestFailed(this);
1589 CMD4Hash hash
= data
.ReadHash();
1590 CPartFile
* pPartFile
= theApp
->downloadqueue
->GetFileByID(hash
);
1591 CAICHRequestedData request
= CAICHHashSet::GetAICHReqDetails(this);
1592 uint16 nPart
= data
.ReadUInt16();
1593 if (pPartFile
!= NULL
&& request
.m_pPartFile
== pPartFile
&& request
.m_pClient
== this && nPart
== request
.m_nPart
){
1594 CAICHHash
ahMasterHash(&data
);
1595 if ( (pPartFile
->GetAICHHashset()->GetStatus() == AICH_TRUSTED
|| pPartFile
->GetAICHHashset()->GetStatus() == AICH_VERIFIED
)
1596 && ahMasterHash
== pPartFile
->GetAICHHashset()->GetMasterHash())
1598 if(pPartFile
->GetAICHHashset()->ReadRecoveryData(request
.m_nPart
*PARTSIZE
, &data
)){
1599 // finally all checks passed, everythings seem to be fine
1600 AddDebugLogLineM( false, logAICHTransfer
, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1601 CAICHHashSet::RemoveClientAICHRequest(this);
1602 pPartFile
->AICHRecoveryDataAvailable(request
.m_nPart
);
1605 AddDebugLogLineM( false, logAICHTransfer
, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1608 AddDebugLogLineM( false, logAICHTransfer
, wxT("AICH Packet Answer: Masterhash differs from packethash or hashset has no trusted Masterhash") );
1611 AddDebugLogLineM( false, logAICHTransfer
, wxT("AICH Packet Answer: requested values differ from values in packet") );
1614 CAICHHashSet::ClientAICHRequestFailed(this);
1618 void CUpDownClient::ProcessAICHRequest(const byte
* packet
, uint32 size
)
1620 if (size
!= 16 + 2 + CAICHHash::GetHashSize()) {
1621 throw wxString(wxT("Received AICH Request Packet with wrong size"));
1624 CMemFile
data(packet
, size
);
1626 CMD4Hash hash
= data
.ReadHash();
1627 uint16 nPart
= data
.ReadUInt16();
1628 CAICHHash
ahMasterHash(&data
);
1629 CKnownFile
* pKnownFile
= theApp
->sharedfiles
->GetFileByID(hash
);
1630 if (pKnownFile
!= NULL
){
1631 if (pKnownFile
->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE
&& pKnownFile
->GetAICHHashset()->HasValidMasterHash()
1632 && pKnownFile
->GetAICHHashset()->GetMasterHash() == ahMasterHash
&& pKnownFile
->GetPartCount() > nPart
1633 && pKnownFile
->GetFileSize() > EMBLOCKSIZE
&& pKnownFile
->GetFileSize() - PARTSIZE
*nPart
> EMBLOCKSIZE
)
1635 CMemFile fileResponse
;
1636 fileResponse
.WriteHash(pKnownFile
->GetFileHash());
1637 fileResponse
.WriteUInt16(nPart
);
1638 pKnownFile
->GetAICHHashset()->GetMasterHash().Write(&fileResponse
);
1639 if (pKnownFile
->GetAICHHashset()->CreatePartRecoveryData(nPart
*PARTSIZE
, &fileResponse
)){
1640 AddDebugLogLineM(false, logAICHTransfer
,
1641 CFormat(wxT("AICH Packet Request: Sucessfully created and send recoverydata for '%s' to %s"))
1642 % pKnownFile
->GetFileName() % GetClientFullInfo());
1644 CPacket
* packAnswer
= new CPacket(fileResponse
, OP_EMULEPROT
, OP_AICHANSWER
);
1645 theStats::AddUpOverheadOther(packAnswer
->GetPacketSize());
1646 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1647 SafeSendPacket(packAnswer
);
1650 AddDebugLogLineM(false, logAICHTransfer
,
1651 CFormat(wxT("AICH Packet Request: Failed to create recoverydata for '%s' to %s"))
1652 % pKnownFile
->GetFileName() % GetClientFullInfo());
1655 AddDebugLogLineM(false, logAICHTransfer
,
1656 CFormat(wxT("AICH Packet Request: Failed to create recoverydata - Hashset not ready or requested Hash differs from Masterhash for '%s' to %s"))
1657 % pKnownFile
->GetFileName() % GetClientFullInfo());
1660 AddDebugLogLineM( false, logAICHTransfer
, wxT("AICH Packet Request: Failed to find requested shared file - ") + GetClientFullInfo() );
1663 CPacket
* packAnswer
= new CPacket(OP_AICHANSWER
, 16, OP_EMULEPROT
);
1664 packAnswer
->Copy16ToDataBuffer(hash
.GetHash());
1665 theStats::AddUpOverheadOther(packAnswer
->GetPacketSize());
1666 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1667 SafeSendPacket(packAnswer
);
1670 void CUpDownClient::ProcessAICHFileHash(CMemFile
* data
, const CPartFile
* file
){
1671 CPartFile
* pPartFile
;
1673 pPartFile
= theApp
->downloadqueue
->GetFileByID(data
->ReadHash());
1675 pPartFile
= (CPartFile
*)file
;
1677 CAICHHash
ahMasterHash(data
);
1679 if(pPartFile
!= NULL
&& pPartFile
== GetRequestFile()){
1680 SetReqFileAICHHash(new CAICHHash(ahMasterHash
));
1681 pPartFile
->GetAICHHashset()->UntrustedHashReceived(ahMasterHash
, GetConnectIP());
1683 AddDebugLogLineM( false, logAICHTransfer
, wxT("ProcessAICHFileHash(): PartFile not found or Partfile differs from requested file, ") + GetClientFullInfo() );
1686 // File_checked_for_headers