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
*PARTSIZE
,((done
+1)*PARTSIZE
)-1)){
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 theStats::AddDownloadingSource();
535 } else if (m_nDownloadState
== DS_DOWNLOADING
) {
536 theStats::RemoveDownloadingSource();
539 if (m_nDownloadState
== DS_DOWNLOADING
) {
540 m_nDownloadState
= byNewState
;
541 ClearDownloadBlockRequests();
544 bytesReceivedCycle
= 0;
546 if (byNewState
== DS_NONE
) {
548 m_reqfile
->UpdatePartsFrequency( this, false ); // Decrement
550 m_downPartStatus
.clear();
553 if (m_socket
&& byNewState
!= DS_ERROR
) {
554 m_socket
->DisableDownloadLimit();
557 m_nDownloadState
= byNewState
;
558 if(GetDownloadState() == DS_DOWNLOADING
) {
559 if (IsEmuleClient()) {
560 SetRemoteQueueFull(false);
562 SetRemoteQueueRank(0); // eMule 0.30c set like this ...
564 UpdateDisplayedInfo(true);
567 /* eMule 0.30c implementation, i give it a try (Creteil) END ... */
569 void CUpDownClient::ProcessHashSet(const byte
* packet
, uint32 size
)
571 if ((!m_reqfile
) || md4cmp(packet
,m_reqfile
->GetFileHash().GetHash())) {
572 throw wxString(wxT("Wrong fileid sent (ProcessHashSet)"));
574 if (!m_fHashsetRequesting
) {
575 throw wxString(wxT("Received unsolicited hashset, ignoring it."));
577 CMemFile
data(packet
,size
);
578 if (m_reqfile
->LoadHashsetFromFile(&data
,true)) {
579 m_fHashsetRequesting
= 0;
581 m_reqfile
->SetHashSetNeeded(true);
582 throw wxString(wxT("Corrupted or invalid hashset received"));
584 SendStartupLoadReq();
587 void CUpDownClient::SendBlockRequests()
589 uint32 current_time
= ::GetTickCount();
592 // Ask new blocks only when all completed
593 if (m_PendingBlocks_list
.size()) {
597 if ((m_dwLastBlockReceived
+ SEC2MS(5)) > current_time
) {
598 // We received last block in less than 5 secs? Let's request faster.
599 m_MaxBlockRequests
= m_MaxBlockRequests
<< 1;
600 if ( m_MaxBlockRequests
> 0x20) {
601 m_MaxBlockRequests
= 0x20;
604 m_MaxBlockRequests
= m_MaxBlockRequests
>> 1;
605 if ( m_MaxBlockRequests
< STANDARD_BLOCKS_REQUEST
) {
606 m_MaxBlockRequests
= STANDARD_BLOCKS_REQUEST
;
611 m_dwLastBlockReceived
= current_time
;
617 uint8 version
= GetVBTTags() ? 2 : 1;
619 if (m_DownloadBlocks_list
.empty()) {
620 // Barry - instead of getting 3, just get how many is needed
621 uint16 count
= m_MaxBlockRequests
- m_PendingBlocks_list
.size();
622 std::vector
<Requested_Block_Struct
*> toadd(count
);
623 if (m_reqfile
->GetNextRequestedBlock(this,&(toadd
[0]),&count
)) {
624 for (int i
= 0; i
!= count
; i
++) {
625 m_DownloadBlocks_list
.push_back(toadd
[i
]);
630 // Barry - Why are unfinished blocks requested again, not just new ones?
632 while (m_PendingBlocks_list
.size() < m_MaxBlockRequests
&& !m_DownloadBlocks_list
.empty()) {
633 Pending_Block_Struct
* pblock
= new Pending_Block_Struct
;
634 pblock
->block
= m_DownloadBlocks_list
.front();
635 pblock
->zStream
= NULL
;
636 pblock
->totalUnzipped
= 0;
637 pblock
->fZStreamError
= 0;
638 pblock
->fRecovered
= 0;
639 m_PendingBlocks_list
.push_back(pblock
);
640 m_DownloadBlocks_list
.pop_front();
644 if (m_PendingBlocks_list
.empty()) {
646 CUpDownClient
* slower_client
= NULL
;
648 if (thePrefs::GetDropSlowSources()) {
649 slower_client
= m_reqfile
->GetSlowerDownloadingClient(m_lastaverage
, this);
652 if (slower_client
== NULL
) {
653 slower_client
= this;
656 if (!slower_client
->GetSentCancelTransfer()) {
657 CPacket
* packet
= new CPacket(OP_CANCELTRANSFER
, 0, OP_EDONKEYPROT
);
658 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
659 if (slower_client
!= this) {
660 // printf("Dropped client %p to allow client %p to download\n",slower_client, this);
662 slower_client
->ClearDownloadBlockRequests();
663 slower_client
->SendPacket(packet
,true,true);
664 slower_client
->SetSentCancelTransfer(1);
667 slower_client
->SetDownloadState(DS_NONEEDEDPARTS
);
669 if (slower_client
!= this) {
670 // Re-request freed blocks.
671 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER (faster source eager to transfer) to ") + slower_client
->GetFullIP() );
672 wxASSERT(m_DownloadBlocks_list
.empty());
673 wxASSERT(m_PendingBlocks_list
.empty());
674 uint16 count
= m_MaxBlockRequests
;
675 std::vector
<Requested_Block_Struct
*> toadd(count
);
676 if (m_reqfile
->GetNextRequestedBlock(this, &(toadd
[0]),&count
)) {
677 for (int i
= 0; i
!= count
; i
++) {
678 Pending_Block_Struct
* pblock
= new Pending_Block_Struct
;
679 pblock
->block
= toadd
[i
];
680 pblock
->zStream
= NULL
;
681 pblock
->totalUnzipped
= 0;
682 pblock
->fZStreamError
= 0;
683 pblock
->fRecovered
= 0;
684 m_PendingBlocks_list
.push_back(pblock
);
687 // WTF, we just freed blocks.
693 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER (no free blocks) to ") + GetFullIP() );
694 //#warning Kry - Would be nice to swap A4AF here.
699 CPacket
* packet
= NULL
;
704 // Most common scenario: hash + blocks to request + every one
705 // having 2 uint32 tags
707 uint8 nBlocks
= m_PendingBlocks_list
.size();
708 if (nBlocks
> m_MaxBlockRequests
) {
709 nBlocks
= m_MaxBlockRequests
;
712 CMemFile
data(16 + 1 + nBlocks
*((2+4)*2));
714 data
.WriteHash(m_reqfile
->GetFileHash());
716 data
.WriteUInt8(nBlocks
);
718 std::list
<Pending_Block_Struct
*>::iterator it
= m_PendingBlocks_list
.begin();
720 wxASSERT(it
!= m_PendingBlocks_list
.end());
721 wxASSERT( (*it
)->block
->StartOffset
<= (*it
)->block
->EndOffset
);
722 (*it
)->fZStreamError
= 0;
723 (*it
)->fRecovered
= 0;
724 CTagVarInt(/*Noname*/0,(*it
)->block
->StartOffset
).WriteTagToFile(&data
);
725 CTagVarInt(/*Noname*/0,(*it
)->block
->EndOffset
).WriteTagToFile(&data
);
730 packet
= new CPacket(data
, OP_ED2KV2HEADER
, OP_REQUESTPARTS
);
731 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() );
736 wxASSERT(m_MaxBlockRequests
== STANDARD_BLOCKS_REQUEST
);
738 //#warning Kry - I dont specially like this approach, we iterate one time too many
740 bool bHasLongBlocks
= false;
742 std::list
<Pending_Block_Struct
*>::iterator it
= m_PendingBlocks_list
.begin();
743 for (uint32 i
= 0; i
!= m_MaxBlockRequests
; i
++){
744 if (it
!= m_PendingBlocks_list
.end()) {
745 Pending_Block_Struct
* pending
= *it
++;
746 wxASSERT( pending
->block
->StartOffset
<= pending
->block
->EndOffset
);
747 if (pending
->block
->StartOffset
> 0xFFFFFFFF || pending
->block
->EndOffset
> 0xFFFFFFFF){
748 bHasLongBlocks
= true;
749 if (!SupportsLargeFiles()){
750 // Requesting a large block from a client that doesn't support large files?
752 if (!GetSentCancelTransfer()){
753 CPacket
* cancel_packet
= new CPacket(OP_CANCELTRANSFER
, 0, OP_EDONKEYPROT
);
754 theStats::AddUpOverheadFileRequest(cancel_packet
->GetPacketSize());
755 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
756 SendPacket(cancel_packet
,true,true);
757 SetSentCancelTransfer(1);
759 SetDownloadState(DS_ERROR
);
766 CMemFile
data(16 /*Hash*/ + (m_MaxBlockRequests
*(bHasLongBlocks
? 8 : 4) /* uint32/64 start*/) + (3*(bHasLongBlocks
? 8 : 4)/* uint32/64 end*/));
767 data
.WriteHash(m_reqfile
->GetFileHash());
769 it
= m_PendingBlocks_list
.begin();
770 for (uint32 i
= 0; i
!= m_MaxBlockRequests
; i
++) {
771 if (it
!= m_PendingBlocks_list
.end()) {
772 Pending_Block_Struct
* pending
= *it
++;
773 wxASSERT( pending
->block
->StartOffset
<= pending
->block
->EndOffset
);
774 pending
->fZStreamError
= 0;
775 pending
->fRecovered
= 0;
776 if (bHasLongBlocks
) {
777 data
.WriteUInt64(pending
->block
->StartOffset
);
779 data
.WriteUInt32(pending
->block
->StartOffset
);
782 if (bHasLongBlocks
) {
790 it
= m_PendingBlocks_list
.begin();
791 for (uint32 i
= 0; i
!= m_MaxBlockRequests
; i
++) {
792 if (it
!= m_PendingBlocks_list
.end()) {
793 Requested_Block_Struct
* block
= (*it
++)->block
;
794 if (bHasLongBlocks
) {
795 data
.WriteUInt64(block
->EndOffset
+1);
797 data
.WriteUInt32(block
->EndOffset
+1);
800 if (bHasLongBlocks
) {
807 packet
= new CPacket(data
, (bHasLongBlocks
? OP_EMULEPROT
: OP_EDONKEYPROT
), (bHasLongBlocks
? (uint8
)OP_REQUESTPARTS_I64
: (uint8
)OP_REQUESTPARTS
));
808 AddDebugLogLineM( false, logLocalClient
, wxString::Format(wxT("Local Client: %s to "),(bHasLongBlocks
? wxT("OP_REQUESTPARTS_I64") : wxT("OP_REQUESTPARTS"))) + GetFullIP() );
816 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
817 SendPacket(packet
, true, true);
824 Barry - Originally this only wrote to disk when a full 180k block
825 had been received from a client, and only asked for data in
828 This meant that on average 90k was lost for every connection
829 to a client data source. That is a lot of wasted data.
831 To reduce the lost data, packets are now written to a buffer
832 and flushed to disk regularly regardless of size downloaded.
834 This includes compressed packets.
836 Data is also requested only where gaps are, not in 180k blocks.
837 The requests will still not exceed 180k, but may be smaller to
841 void CUpDownClient::ProcessBlockPacket(const byte
* packet
, uint32 size
, bool packed
, bool largeblocks
)
843 // Ignore if no data required
844 if (!(GetDownloadState() == DS_DOWNLOADING
|| GetDownloadState() == DS_NONEEDEDPARTS
)) {
848 // This vars are defined here to be able to use them on the catch
849 int header_size
= 16;
850 uint64 nStartPos
= 0;
852 uint32 nBlockSize
= 0;
853 uint32 lenUnzipped
= 0;
856 m_dwLastBlockReceived
= ::GetTickCount();
860 // Read data from packet
861 const CMemFile
data(packet
, size
);
863 // Check that this data is for the correct file
864 if ((!m_reqfile
) || data
.ReadHash() != m_reqfile
->GetFileHash()) {
865 throw wxString(wxT("Wrong fileid sent (ProcessBlockPacket)"));
868 // Find the start & end positions, and size of this chunk of data
871 nStartPos
= data
.ReadUInt64();
874 nStartPos
= data
.ReadUInt32();
879 nBlockSize
= data
.ReadUInt32();
881 nEndPos
= nStartPos
+ (size
- header_size
);
884 nEndPos
= data
.ReadUInt64();
887 nEndPos
= data
.ReadUInt32();
892 // Check that packet size matches the declared data size + header size
893 if ( nEndPos
== nStartPos
|| size
!= ((nEndPos
- nStartPos
) + header_size
)) {
894 throw wxString(wxT("Corrupted or invalid DataBlock received (ProcessBlockPacket)"));
896 theStats::AddDownloadFromSoft(GetClientSoft(),size
- header_size
);
897 bytesReceivedCycle
+= size
- header_size
;
899 credits
->AddDownloaded(size
- header_size
, GetIP(), theApp
->CryptoAvailable());
901 // Move end back one, should be inclusive
904 // Loop through to find the reserved block that this is within
905 std::list
<Pending_Block_Struct
*>::iterator it
= m_PendingBlocks_list
.begin();
906 for (; it
!= m_PendingBlocks_list
.end(); ++it
) {
907 Pending_Block_Struct
* cur_block
= *it
;
909 if ((cur_block
->block
->StartOffset
<= nStartPos
) && (cur_block
->block
->EndOffset
>= nStartPos
)) {
910 // Found reserved block
912 if (cur_block
->block
->StartOffset
== nStartPos
) {
913 // This block just started transfering. Set the start time.
914 m_last_block_start
= ::GetTickCountFullRes();
917 if (cur_block
->fZStreamError
){
918 AddDebugLogLineM( false, logZLib
,
919 CFormat(wxT("Ignoring %u bytes of block %u-%u because of erroneous zstream state for file: %s"))
920 % (size
- header_size
) % nStartPos
% nEndPos
% m_reqfile
->GetFileName());
921 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
925 // Remember this start pos, used to draw part downloading in list
926 m_nLastBlockOffset
= nStartPos
;
928 // Occasionally packets are duplicated, no point writing it twice
929 // This will be 0 in these cases, or the length written otherwise
930 uint32 lenWritten
= 0;
932 // Handle differently depending on whether packed or not
934 // security sanitize check
935 if (nEndPos
> cur_block
->block
->EndOffset
) {
936 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()));
937 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
940 // Write to disk (will be buffered in part file class)
941 lenWritten
= m_reqfile
->WriteToBuffer( size
- header_size
, (byte
*)(packet
+ header_size
), nStartPos
, nEndPos
, cur_block
->block
);
944 wxASSERT( (long int)size
> 0 );
945 // Create space to store unzipped data, the size is
946 // only an initial guess, will be resized in unzip()
948 lenUnzipped
= (size
* 2);
950 if (lenUnzipped
> (BLOCKSIZE
+ 300)) {
951 lenUnzipped
= (BLOCKSIZE
+ 300);
953 byte
*unzipped
= new byte
[lenUnzipped
];
955 // Try to unzip the packet
956 int result
= unzip(cur_block
, (byte
*)(packet
+ header_size
), (size
- header_size
), &unzipped
, &lenUnzipped
);
958 // no block can be uncompressed to >2GB, 'lenUnzipped' is obviously erroneous.
959 if (result
== Z_OK
&& ((int)lenUnzipped
>= 0)) {
961 // Write any unzipped data to disk
962 if (lenUnzipped
> 0) {
963 wxASSERT( (int)lenUnzipped
> 0 );
965 // Use the current start and end positions for the uncompressed data
966 nStartPos
= cur_block
->block
->StartOffset
+ cur_block
->totalUnzipped
- lenUnzipped
;
967 nEndPos
= cur_block
->block
->StartOffset
+ cur_block
->totalUnzipped
- 1;
969 if (nStartPos
> cur_block
->block
->EndOffset
|| nEndPos
> cur_block
->block
->EndOffset
) {
970 AddDebugLogLineM( false, logZLib
,
971 CFormat(wxT("Corrupted compressed packet for '%s' received (error 666)")) % m_reqfile
->GetFileName());
972 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
974 // Write uncompressed data to file
975 lenWritten
= m_reqfile
->WriteToBuffer( size
- header_size
,
983 wxString strZipError
;
984 if (cur_block
->zStream
&& cur_block
->zStream
->msg
) {
985 strZipError
= wxT(" - ") + wxString::FromAscii(cur_block
->zStream
->msg
);
988 AddDebugLogLineM( false, logZLib
,
989 CFormat(wxT("Corrupted compressed packet for '%s' received (error %i): %s"))
990 % m_reqfile
->GetFileName() % result
% strZipError
);
992 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
994 // If we had an zstream error, there is no chance that we could recover from it nor that we
995 // could use the current zstream (which is in error state) any longer.
996 if (cur_block
->zStream
){
997 inflateEnd(cur_block
->zStream
);
998 delete cur_block
->zStream
;
999 cur_block
->zStream
= NULL
;
1002 // Although we can't further use the current zstream, there is no need to disconnect the sending
1003 // client because the next zstream (a series of 10K-blocks which build a 180K-block) could be
1004 // valid again. Just ignore all further blocks for the current zstream.
1005 cur_block
->fZStreamError
= 1;
1006 cur_block
->totalUnzipped
= 0; // bluecow's fix
1010 // These checks only need to be done if any data was written
1011 if (lenWritten
> 0) {
1012 m_nTransferredDown
+= lenWritten
;
1014 // If finished reserved block
1015 if (nEndPos
== cur_block
->block
->EndOffset
) {
1017 // Save last average speed based on data and time.
1018 // This should do bytes/sec.
1019 uint32 average_time
= (::GetTickCountFullRes() - m_last_block_start
);
1021 // Avoid divide by 0.
1022 if (average_time
== 0) {
1026 m_lastaverage
= ((cur_block
->block
->EndOffset
- cur_block
->block
->StartOffset
) * 1000) / average_time
;
1028 m_reqfile
->RemoveBlockFromList(cur_block
->block
->StartOffset
, cur_block
->block
->EndOffset
);
1029 delete cur_block
->block
;
1030 // Not always allocated
1031 if (cur_block
->zStream
) {
1032 inflateEnd(cur_block
->zStream
);
1033 delete cur_block
->zStream
;
1036 m_PendingBlocks_list
.erase(it
);
1038 // Request next block
1039 SendBlockRequests();
1042 // Stop looping and exit method
1046 } catch (const CEOFException
& e
) {
1047 wxString error
= wxString(wxT("Error reading "));
1048 if (packed
) error
+= wxString::Format(wxT("packed (LU: %i) "),lenUnzipped
);
1049 if (packed
) error
+= wxT("largeblocks ");
1050 error
+= wxString::Format(wxT("data packet: RS: %i HS: %i SP: %i EP: %i BS: %i -> "),size
,header_size
,nStartPos
,nEndPos
,nBlockSize
);
1051 AddDebugLogLineM(true, logRemoteClient
, error
+ e
.what());
1056 int CUpDownClient::unzip(Pending_Block_Struct
*block
, byte
*zipped
, uint32 lenZipped
, byte
**unzipped
, uint32
*lenUnzipped
, int iRecursion
)
1058 int err
= Z_DATA_ERROR
;
1061 z_stream
*zS
= block
->zStream
;
1063 // Is this the first time this block has been unzipped
1066 block
->zStream
= new z_stream
;
1067 zS
= block
->zStream
;
1069 // Initialise stream values
1070 zS
->zalloc
= (alloc_func
)0;
1071 zS
->zfree
= (free_func
)0;
1072 zS
->opaque
= (voidpf
)0;
1074 // Set output data streams, do this here to avoid overwriting on recursive calls
1075 zS
->next_out
= (*unzipped
);
1076 zS
->avail_out
= (*lenUnzipped
);
1078 // Initialise the z_stream
1079 err
= inflateInit(zS
);
1085 // Use whatever input is provided
1086 zS
->next_in
= zipped
;
1087 zS
->avail_in
= lenZipped
;
1089 // Only set the output if not being called recursively
1090 if (iRecursion
== 0) {
1091 zS
->next_out
= (*unzipped
);
1092 zS
->avail_out
= (*lenUnzipped
);
1095 // Try to unzip the data
1096 err
= inflate(zS
, Z_SYNC_FLUSH
);
1098 // Is zip finished reading all currently available input and writing
1099 // all generated output
1100 if (err
== Z_STREAM_END
) {
1102 err
= inflateEnd(zS
);
1107 // Got a good result, set the size to the amount unzipped in this call
1108 // (including all recursive calls)
1109 (*lenUnzipped
) = (zS
->total_out
- block
->totalUnzipped
);
1110 block
->totalUnzipped
= zS
->total_out
;
1111 } else if ((err
== Z_OK
) && (zS
->avail_out
== 0) && (zS
->avail_in
!= 0)) {
1113 // Output array was not big enough,
1114 // call recursively until there is enough space
1116 // What size should we try next
1117 uint32 newLength
= (*lenUnzipped
) *= 2;
1118 if (newLength
== 0) {
1119 newLength
= lenZipped
* 2;
1121 // Copy any data that was successfully unzipped to new array
1122 byte
*temp
= new byte
[newLength
];
1123 wxASSERT( zS
->total_out
- block
->totalUnzipped
<= newLength
);
1124 memcpy(temp
, (*unzipped
), (zS
->total_out
- block
->totalUnzipped
));
1125 delete [] (*unzipped
);
1127 (*lenUnzipped
) = newLength
;
1129 // Position stream output to correct place in new array
1130 zS
->next_out
= (*unzipped
) + (zS
->total_out
- block
->totalUnzipped
);
1131 zS
->avail_out
= (*lenUnzipped
) - (zS
->total_out
- block
->totalUnzipped
);
1134 err
= unzip(block
, zS
->next_in
, zS
->avail_in
, unzipped
, lenUnzipped
, iRecursion
+ 1);
1135 } else if ((err
== Z_OK
) && (zS
->avail_in
== 0)) {
1136 // All available input has been processed, everything ok.
1137 // Set the size to the amount unzipped in this call
1138 // (including all recursive calls)
1139 (*lenUnzipped
) = (zS
->total_out
- block
->totalUnzipped
);
1140 block
->totalUnzipped
= zS
->total_out
;
1142 // Should not get here unless input data is corrupt
1143 wxString strZipError
;
1146 strZipError
= wxString::Format(wxT(" %d '"), err
) + wxString::FromAscii(zS
->msg
) + wxT("'");
1147 } else if (err
!= Z_OK
) {
1148 strZipError
= wxString::Format(wxT(" %d"), err
);
1151 AddDebugLogLineM(false, logZLib
,
1152 CFormat(wxT("Unexpected zip error %s in file '%s'"))
1153 % strZipError
% (m_reqfile
? m_reqfile
->GetFileName() : CPath(wxT("?"))));
1164 float CUpDownClient::GetKBpsDown() const
1166 return kBpsDown
* theStats::GetDownloadRateAdjust();
1170 // Emilio: rewrite of eMule code to eliminate use of lists for averaging and fix
1171 // errors in calculation (32-bit rollover and time measurement) This function
1172 // uses a first-order filter with variable time constant (initially very short
1173 // to quickly reach the right value without spiking, then gradually approaching
1174 // the value of 50 seconds which is equivalent to the original averaging period
1175 // used in eMule). The download rate is measured using actual timestamps. The
1176 // filter-based averaging however uses a simplified algorithm that assumes a
1177 // fixed loop time - this does not introduce any measurement error, it simply
1178 // makes the degree of smoothing slightly imprecise (the true TC of the filter
1179 // varies inversely with the true loop time), which is of no importance here.
1181 float CUpDownClient::CalculateKBpsDown()
1183 // -- all timing values are in seconds --
1184 const float tcLoop
= 0.1f
; // _assumed_ Process() loop time = 0.1 sec
1185 const float tcInit
= 0.4f
; // initial filter time constant
1186 const float tcFinal
= 50.0f
; // final filter time constant
1187 const float tcReduce
= 5.0f
; // transition from tcInit to tcFinal
1189 const float fInit
= tcLoop
/tcInit
; // initial averaging factor
1190 const float fFinal
= tcLoop
/tcFinal
; // final averaging factor
1191 const float fReduce
= std::exp(std::log(fFinal
/fInit
) / (tcReduce
/tcLoop
)) * 0.99999;
1193 uint32 msCur
= ::GetTickCount();
1195 if (msReceivedPrev
== 0) { // initialize the averaging filter
1196 fDownAvgFilter
= fInit
;
1197 // "kBpsDown = bytesReceivedCycle/1024.0 / tcLoop" would be technically correct,
1198 // but the first loop often receives a large chunk of data and then produces a spike
1199 kBpsDown
= /* 0.0 * (1.0-fInit) + */ bytesReceivedCycle
/1024.0 / tcLoop
* fInit
;
1200 bytesReceivedCycle
= 0;
1201 } else if (msCur
!= msReceivedPrev
) { // (safeguard against divide-by-zero)
1202 if (fDownAvgFilter
> fFinal
) { // reduce time constant during ramp-up phase
1203 fDownAvgFilter
*= fReduce
; // this approximates averaging a lengthening list
1205 kBpsDown
= kBpsDown
* (1.0 - fDownAvgFilter
)
1206 + (bytesReceivedCycle
/1.024)/((float)(msCur
-msReceivedPrev
)) * fDownAvgFilter
;
1207 bytesReceivedCycle
= 0;
1209 msReceivedPrev
= msCur
;
1212 if (m_cShowDR
== 30){
1214 UpdateDisplayedInfo();
1216 if ((::GetTickCount() - m_dwLastBlockReceived
) > DOWNLOADTIMEOUT
){
1217 if (!GetSentCancelTransfer()){
1218 CPacket
* packet
= new CPacket(OP_CANCELTRANSFER
, 0, OP_EDONKEYPROT
);
1219 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
1220 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
1221 SendPacket(packet
,true,true);
1222 SetSentCancelTransfer(1);
1224 SetDownloadState(DS_ONQUEUE
);
1230 uint16
CUpDownClient::GetAvailablePartCount() const
1233 for (int i
= 0;i
!= m_nPartCount
;i
++){
1234 if (IsPartAvailable(i
))
1240 void CUpDownClient::SetRemoteQueueRank(uint16 nr
)
1242 m_nOldRemoteQueueRank
= m_nRemoteQueueRank
;
1243 m_nRemoteQueueRank
= nr
;
1244 UpdateDisplayedInfo();
1247 void CUpDownClient::UDPReaskACK(uint16 nNewQR
)
1250 m_bUDPPending
= false;
1251 SetRemoteQueueRank(nNewQR
);
1252 m_dwLastAskedTime
= ::GetTickCount();
1255 void CUpDownClient::UDPReaskFNF()
1257 m_bUDPPending
= false;
1259 // avoid premature deletion of 'this' client
1260 if (GetDownloadState() != DS_DOWNLOADING
){
1262 m_reqfile
->AddDeadSource(this);
1265 theApp
->downloadqueue
->RemoveSource(this);
1267 if (Disconnected(wxT("UDPReaskFNF m_socket=NULL"))) {
1272 AddDebugLogLineM( false, logRemoteClient
, wxT("UDP ANSWER FNF : ") + GetUserName() + wxT(" - did not remove client because of current download state") );
1276 void CUpDownClient::UDPReaskForDownload()
1279 wxASSERT(m_reqfile
);
1281 if(!m_reqfile
|| m_bUDPPending
) {
1285 //#warning We should implement the quality tests for udp reliability
1287 if( m_nTotalUDPPackets > 3 && ((float)(m_nFailedUDPPackets/m_nTotalUDPPackets) > .3)) {
1292 if (thePrefs::GetEffectiveUDPPort() == 0) {
1296 if (m_nUDPPort
!= 0 && !theApp
->IsFirewalled() && !IsConnected()) {
1297 //don't use udp to ask for sources
1298 if(IsSourceRequestAllowed()) {
1302 m_bUDPPending
= true;
1305 data
.WriteHash(m_reqfile
->GetFileHash());
1307 if (GetUDPVersion() > 3) {
1308 if (m_reqfile
->IsPartFile()) {
1309 ((CPartFile
*)m_reqfile
)->WritePartStatus(&data
);
1312 data
.WriteUInt16(0);
1316 if (GetUDPVersion() > 2) {
1317 data
.WriteUInt16(m_reqfile
->m_nCompleteSourcesCount
);
1320 CPacket
* response
= new CPacket(data
, OP_EMULEPROT
, OP_REASKFILEPING
);
1321 AddDebugLogLineM( false, logClientUDP
, wxT("Client UDP socket: send OP_REASKFILEPING") );
1322 theStats::AddUpOverheadFileRequest(response
->GetPacketSize());
1323 theApp
->clientudp
->SendPacket(response
,GetConnectIP(),GetUDPPort(), ShouldReceiveCryptUDPPackets(), GetUserHash().GetHash(), false, 0);
1324 } else if (HasLowID() && GetBuddyIP() && GetBuddyPort() && HasValidBuddyID()) {
1326 m_bUDPPending
= true;
1330 data
.WriteHash(CMD4Hash(GetBuddyID()));
1331 data
.WriteHash(m_reqfile
->GetFileHash());
1333 if (GetUDPVersion() > 3) {
1334 if (m_reqfile
->IsPartFile()) {
1335 ((CPartFile
*)m_reqfile
)->WritePartStatus(&data
);
1337 data
.WriteUInt16(0);
1341 if (GetUDPVersion() > 2) {
1342 data
.WriteUInt16(m_reqfile
->m_nCompleteSourcesCount
);
1345 CPacket
* response
= new CPacket(data
, OP_EMULEPROT
, OP_REASKCALLBACKUDP
);
1346 AddDebugLogLineM( false, logClientUDP
, wxT("Client UDP socket: send OP_REASKCALLBACKUDP") );
1347 theStats::AddUpOverheadFileRequest(response
->GetPacketSize());
1348 theApp
->clientudp
->SendPacket(response
, GetBuddyIP(), GetBuddyPort(), false, NULL
, true, 0 );
1353 //! Barry - Sets string to show parts downloading, eg NNNYNNNNYYNYN
1354 wxString
CUpDownClient::ShowDownloadingParts() const
1356 // Initialise to all N's
1357 wxString
Parts(wxT('N'), m_nPartCount
);
1359 std::list
<Pending_Block_Struct
*>::const_iterator it
= m_PendingBlocks_list
.begin();
1360 for (; it
!= m_PendingBlocks_list
.end(); ++it
) {
1361 Parts
.SetChar(((*it
)->block
->StartOffset
/ PARTSIZE
), 'Y');
1368 void CUpDownClient::UpdateDisplayedInfo(bool force
)
1370 uint32 curTick
= ::GetTickCount();
1371 if(force
|| curTick
-m_lastRefreshedDLDisplay
> MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE
) {
1372 // Check if we actually need to notify of changes
1373 bool update
= m_reqfile
&& m_reqfile
->ShowSources();
1375 // Check A4AF files only if needed
1377 A4AFList::iterator it
= m_A4AF_list
.begin();
1378 for ( ; it
!= m_A4AF_list
.end(); ++it
) {
1379 if ( it
->first
->ShowSources() ) {
1386 // And finnaly trigger an event if there's any reason
1388 Notify_DownloadCtrlUpdateItem(this);
1391 m_lastRefreshedDLDisplay
= curTick
;
1396 // IgnoreNoNeeded = will switch to files of which this source has no needed parts (if no better fiels found)
1397 // ignoreSuspensions = ignore timelimit for A4Af jumping
1398 // bRemoveCompletely = do not readd the file which the source is swapped from to the A4AF lists (needed if deleting or stopping a file)
1399 // toFile = Try to swap to this partfile only
1401 bool CUpDownClient::SwapToAnotherFile(bool bIgnoreNoNeeded
, bool ignoreSuspensions
, bool bRemoveCompletely
, CPartFile
* toFile
)
1403 // Fail if m_reqfile is invalid
1404 if ( m_reqfile
== NULL
) {
1408 // It would be stupid to swap away a downloading source
1409 if (GetDownloadState() == DS_DOWNLOADING
) {
1413 // The iterator of the final target
1414 A4AFList::iterator target
= m_A4AF_list
.end();
1416 // Do we want to swap to a specific file?
1417 if ( toFile
!= NULL
) {
1418 A4AFList::iterator it
= m_A4AF_list
.find( toFile
);
1419 if ( it
!= m_A4AF_list
.end() ) {
1421 // We force ignoring of noneeded flag and timestamps
1422 if ( IsValidSwapTarget( it
, true, true ) ) {
1428 // We want highest priority possible, but need to start with
1429 // a value less than any other priority
1432 A4AFList::iterator it
= m_A4AF_list
.begin();
1433 for ( ; it
!= m_A4AF_list
.end(); ++it
) {
1434 if ( IsValidSwapTarget( it
, bIgnoreNoNeeded
, ignoreSuspensions
) ) {
1435 char cur_priority
= it
->first
->GetDownPriority();
1437 // We would prefer to get files with needed parts, thus rate them higher.
1438 // However, this really only matters if bIgnoreNoNeeded is true.
1439 if ( it
->second
.NeededParts
)
1442 // Change target if the current file has a higher rate than the previous
1443 if ( cur_priority
> priority
) {
1444 priority
= cur_priority
;
1446 // Set the new target
1449 // Break on the first High-priority file with needed parts
1450 if ( priority
== PR_HIGH
+ 10 ) {
1458 // Try to swap if we found a valid target
1459 if ( target
!= m_A4AF_list
.end() ) {
1461 // Sainity check, if reqfile doesn't own the source, then something
1462 // is wrong and the swap cannot proceed.
1463 if ( m_reqfile
->DelSource( this ) ) {
1464 CPartFile
* SwapTo
= target
->first
;
1466 // remove this client from the A4AF list of our new m_reqfile
1467 if ( SwapTo
->RemoveA4AFSource( this ) ) {
1468 Notify_DownloadCtrlRemoveSource(this, SwapTo
);
1471 m_reqfile
->RemoveDownloadingSource( this );
1473 // Do we want to remove it completly? Say if the old file is getting deleted
1474 if ( !bRemoveCompletely
) {
1475 m_reqfile
->AddA4AFSource( this );
1477 // Set the status of the old file
1478 m_A4AF_list
[m_reqfile
].NeededParts
= (GetDownloadState() != DS_NONEEDEDPARTS
);
1480 // Avoid swapping to this file for a while
1481 m_A4AF_list
[m_reqfile
].timestamp
= ::GetTickCount();
1483 Notify_DownloadCtrlAddSource(m_reqfile
, this, A4AF_SOURCE
);
1485 Notify_DownloadCtrlRemoveSource( this, m_reqfile
);
1488 SetDownloadState(DS_NONE
);
1489 ResetFileStatusInfo();
1491 m_nRemoteQueueRank
= 0;
1492 m_nOldRemoteQueueRank
= 0;
1494 m_reqfile
->UpdatePartsInfo();
1496 SetRequestFile( SwapTo
);
1498 SwapTo
->AddSource( this );
1500 Notify_DownloadCtrlAddSource(SwapTo
, this, UNAVAILABLE_SOURCE
);
1502 // Remove the new reqfile from the list of other files
1503 m_A4AF_list
.erase( target
);
1513 bool CUpDownClient::IsValidSwapTarget( A4AFList::iterator it
, bool ignorenoneeded
, bool ignoresuspended
)
1515 wxASSERT( it
!= m_A4AF_list
.end() && it
->first
);
1517 // Check if this file has been suspended
1518 if ( !ignoresuspended
) {
1519 if ( ::GetTickCount() - it
->second
.timestamp
>= PURGESOURCESWAPSTOP
) {
1520 // The wait-time has been exceeded and the file is now a valid target
1521 it
->second
.timestamp
= 0;
1523 // The file was still suspended and we are not ignoring suspensions
1528 // Check if the client has needed parts
1529 if ( !ignorenoneeded
) {
1530 if ( !it
->second
.NeededParts
) {
1535 // Final checks to see if the client is a valid target
1536 CPartFile
* cur_file
= it
->first
;
1537 if ( ( cur_file
!= m_reqfile
&& !cur_file
->IsStopped() ) &&
1538 ( cur_file
->GetStatus() == PS_READY
|| cur_file
->GetStatus() == PS_EMPTY
) &&
1539 ( cur_file
->IsPartFile() ) )
1548 void CUpDownClient::SetRequestFile(CPartFile
* reqfile
)
1550 if ( m_reqfile
!= reqfile
) {
1551 // Decrement the source-count of the old request-file
1553 m_reqfile
->ClientStateChanged( GetDownloadState(), -1 );
1554 m_reqfile
->UpdatePartsFrequency( this, false );
1558 m_downPartStatus
.clear();
1560 m_reqfile
= reqfile
;
1563 // Increment the source-count of the new request-file
1564 m_reqfile
->ClientStateChanged( -1, GetDownloadState() );
1566 m_nPartCount
= reqfile
->GetPartCount();
1571 void CUpDownClient::SetReqFileAICHHash(CAICHHash
* val
){
1572 if(m_pReqFileAICHHash
!= NULL
&& m_pReqFileAICHHash
!= val
)
1573 delete m_pReqFileAICHHash
;
1574 m_pReqFileAICHHash
= val
;
1577 void CUpDownClient::SendAICHRequest(CPartFile
* pForFile
, uint16 nPart
){
1578 CAICHRequestedData request
;
1579 request
.m_nPart
= nPart
;
1580 request
.m_pClient
= this;
1581 request
.m_pPartFile
= pForFile
;
1582 CAICHHashSet::m_liRequestedData
.push_back(request
);
1583 m_fAICHRequested
= TRUE
;
1585 data
.WriteHash(pForFile
->GetFileHash());
1586 data
.WriteUInt16(nPart
);
1587 pForFile
->GetAICHHashset()->GetMasterHash().Write(&data
);
1588 CPacket
* packet
= new CPacket(data
, OP_EMULEPROT
, OP_AICHREQUEST
);
1589 theStats::AddUpOverheadOther(packet
->GetPacketSize());
1590 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_AICHREQUEST to") + GetFullIP());
1591 SafeSendPacket(packet
);
1594 void CUpDownClient::ProcessAICHAnswer(const byte
* packet
, uint32 size
)
1596 if (m_fAICHRequested
== FALSE
){
1597 throw wxString(wxT("Received unrequested AICH Packet"));
1599 m_fAICHRequested
= FALSE
;
1601 CMemFile
data(packet
, size
);
1603 CAICHHashSet::ClientAICHRequestFailed(this);
1607 CMD4Hash hash
= data
.ReadHash();
1608 CPartFile
* pPartFile
= theApp
->downloadqueue
->GetFileByID(hash
);
1609 CAICHRequestedData request
= CAICHHashSet::GetAICHReqDetails(this);
1610 uint16 nPart
= data
.ReadUInt16();
1611 if (pPartFile
!= NULL
&& request
.m_pPartFile
== pPartFile
&& request
.m_pClient
== this && nPart
== request
.m_nPart
){
1612 CAICHHash
ahMasterHash(&data
);
1613 if ( (pPartFile
->GetAICHHashset()->GetStatus() == AICH_TRUSTED
|| pPartFile
->GetAICHHashset()->GetStatus() == AICH_VERIFIED
)
1614 && ahMasterHash
== pPartFile
->GetAICHHashset()->GetMasterHash())
1616 if(pPartFile
->GetAICHHashset()->ReadRecoveryData(request
.m_nPart
*PARTSIZE
, &data
)){
1617 // finally all checks passed, everythings seem to be fine
1618 AddDebugLogLineM( false, logAICHTransfer
, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1619 CAICHHashSet::RemoveClientAICHRequest(this);
1620 pPartFile
->AICHRecoveryDataAvailable(request
.m_nPart
);
1623 AddDebugLogLineM( false, logAICHTransfer
, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1626 AddDebugLogLineM( false, logAICHTransfer
, wxT("AICH Packet Answer: Masterhash differs from packethash or hashset has no trusted Masterhash") );
1629 AddDebugLogLineM( false, logAICHTransfer
, wxT("AICH Packet Answer: requested values differ from values in packet") );
1632 CAICHHashSet::ClientAICHRequestFailed(this);
1636 void CUpDownClient::ProcessAICHRequest(const byte
* packet
, uint32 size
)
1638 if (size
!= 16 + 2 + CAICHHash::GetHashSize()) {
1639 throw wxString(wxT("Received AICH Request Packet with wrong size"));
1642 CMemFile
data(packet
, size
);
1644 CMD4Hash hash
= data
.ReadHash();
1645 uint16 nPart
= data
.ReadUInt16();
1646 CAICHHash
ahMasterHash(&data
);
1647 CKnownFile
* pKnownFile
= theApp
->sharedfiles
->GetFileByID(hash
);
1648 if (pKnownFile
!= NULL
){
1649 if (pKnownFile
->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE
&& pKnownFile
->GetAICHHashset()->HasValidMasterHash()
1650 && pKnownFile
->GetAICHHashset()->GetMasterHash() == ahMasterHash
&& pKnownFile
->GetPartCount() > nPart
1651 && pKnownFile
->GetFileSize() > EMBLOCKSIZE
&& pKnownFile
->GetFileSize() - PARTSIZE
*nPart
> EMBLOCKSIZE
)
1653 CMemFile fileResponse
;
1654 fileResponse
.WriteHash(pKnownFile
->GetFileHash());
1655 fileResponse
.WriteUInt16(nPart
);
1656 pKnownFile
->GetAICHHashset()->GetMasterHash().Write(&fileResponse
);
1657 if (pKnownFile
->GetAICHHashset()->CreatePartRecoveryData(nPart
*PARTSIZE
, &fileResponse
)){
1658 AddDebugLogLineM(false, logAICHTransfer
,
1659 CFormat(wxT("AICH Packet Request: Sucessfully created and send recoverydata for '%s' to %s"))
1660 % pKnownFile
->GetFileName() % GetClientFullInfo());
1662 CPacket
* packAnswer
= new CPacket(fileResponse
, OP_EMULEPROT
, OP_AICHANSWER
);
1663 theStats::AddUpOverheadOther(packAnswer
->GetPacketSize());
1664 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1665 SafeSendPacket(packAnswer
);
1668 AddDebugLogLineM(false, logAICHTransfer
,
1669 CFormat(wxT("AICH Packet Request: Failed to create recoverydata for '%s' to %s"))
1670 % pKnownFile
->GetFileName() % GetClientFullInfo());
1673 AddDebugLogLineM(false, logAICHTransfer
,
1674 CFormat(wxT("AICH Packet Request: Failed to create recoverydata - Hashset not ready or requested Hash differs from Masterhash for '%s' to %s"))
1675 % pKnownFile
->GetFileName() % GetClientFullInfo());
1678 AddDebugLogLineM( false, logAICHTransfer
, wxT("AICH Packet Request: Failed to find requested shared file - ") + GetClientFullInfo() );
1681 CPacket
* packAnswer
= new CPacket(OP_AICHANSWER
, 16, OP_EMULEPROT
);
1682 packAnswer
->Copy16ToDataBuffer(hash
.GetHash());
1683 theStats::AddUpOverheadOther(packAnswer
->GetPacketSize());
1684 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1685 SafeSendPacket(packAnswer
);
1688 void CUpDownClient::ProcessAICHFileHash(CMemFile
* data
, const CPartFile
* file
){
1689 CPartFile
* pPartFile
;
1691 pPartFile
= theApp
->downloadqueue
->GetFileByID(data
->ReadHash());
1693 pPartFile
= (CPartFile
*)file
;
1695 CAICHHash
ahMasterHash(data
);
1697 if(pPartFile
!= NULL
&& pPartFile
== GetRequestFile()){
1698 SetReqFileAICHHash(new CAICHHash(ahMasterHash
));
1699 pPartFile
->GetAICHHashset()->UntrustedHashReceived(ahMasterHash
, GetConnectIP());
1701 AddDebugLogLineM( false, logAICHTransfer
, wxT("ProcessAICHFileHash(): PartFile not found or Partfile differs from requested file, ") + GetClientFullInfo() );
1704 // File_checked_for_headers