Forwardport from 2.2.3: Fix autosort happening when it shouldn't
[amule.git] / src / DownloadClient.cpp
blob96c0a9103e0944461b654315322cbc0e38e6816b
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "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>
35 #include <zlib.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
50 #include "Logger.h"
51 #include "GuiEvents.h" // Needed for Notify_*
52 #include "UploadQueue.h" // Needed for CUploadQueue
55 #ifdef __MULE_UNUSED_CODE__
56 // This function is left as a reminder.
57 // Changes here _must_ be reflected in CClientList::FindMatchingClient.
58 bool CUpDownClient::Compare(const CUpDownClient* tocomp, bool bIgnoreUserhash) const
60 if (!tocomp) {
61 // should we wxASSERT here?
62 return false;
65 //Compare only the user hash..
66 if(!bIgnoreUserhash && HasValidHash() && tocomp->HasValidHash()) {
67 return GetUserHash() == tocomp->GetUserHash();
70 if (HasLowID()) {
71 //User is firewalled.. Must do two checks..
72 if (GetIP()!=0 && GetIP() == tocomp->GetIP()) {
73 //The IP of both match
74 if (GetUserPort()!=0 && GetUserPort() == tocomp->GetUserPort()) {
75 //IP-UserPort matches
76 return true;
78 if (GetKadPort()!=0 && GetKadPort() == tocomp->GetKadPort()) {
79 //IP-KadPort Matches
80 return true;
84 if (GetUserIDHybrid()!=0
85 && GetUserIDHybrid() == tocomp->GetUserIDHybrid()
86 && GetServerIP()!=0
87 && GetServerIP() == tocomp->GetServerIP()
88 && GetServerPort()!=0
89 && GetServerPort() == tocomp->GetServerPort()) {
90 //Both have the same lowID, Same serverIP and Port..
91 return true;
94 //Both IP, and Server do not match..
95 return false;
98 //User is not firewalled.
99 if (GetUserPort()!=0) {
100 //User has a Port, lets check the rest.
101 if (GetIP() != 0 && tocomp->GetIP() != 0) {
102 //Both clients have a verified IP..
103 if(GetIP() == tocomp->GetIP() && GetUserPort() == tocomp->GetUserPort()) {
104 //IP and UserPort match..
105 return true;
107 } else {
108 //One of the two clients do not have a verified IP
109 if (GetUserIDHybrid() == tocomp->GetUserIDHybrid() && GetUserPort() == tocomp->GetUserPort()) {
110 //ID and Port Match..
111 return true;
116 if(GetKadPort()!=0) {
117 //User has a Kad Port.
118 if(GetIP() != 0 && tocomp->GetIP() != 0) {
119 //Both clients have a verified IP.
120 if(GetIP() == tocomp->GetIP() && GetKadPort() == tocomp->GetKadPort()) {
121 //IP and KadPort Match..
122 return true;
124 } else {
125 //One of the users do not have a verified IP.
126 if (GetUserIDHybrid() == tocomp->GetUserIDHybrid() && GetKadPort() == tocomp->GetKadPort()) {
127 //ID and KadProt Match..
128 return true;
133 //No Matches..
134 return false;
136 #endif
139 bool CUpDownClient::AskForDownload()
141 // 0.42e
142 if (theApp->listensocket->TooManySockets()) {
143 if (!m_socket) {
144 if (GetDownloadState() != DS_TOOMANYCONNS) {
145 SetDownloadState(DS_TOOMANYCONNS);
147 return true;
148 } else if (!m_socket->IsConnected()) {
149 if (GetDownloadState() != DS_TOOMANYCONNS) {
150 SetDownloadState(DS_TOOMANYCONNS);
152 return true;
155 m_bUDPPending = false;
156 m_dwLastAskedTime = ::GetTickCount();
157 SetDownloadState(DS_CONNECTING);
158 SetSentCancelTransfer(0);
159 return TryToConnect();
163 void CUpDownClient::SendStartupLoadReq()
165 // 0.42e
166 if (m_socket==NULL || m_reqfile==NULL) {
167 return;
169 SetDownloadState(DS_ONQUEUE);
170 CMemFile dataStartupLoadReq(16);
171 dataStartupLoadReq.WriteHash(m_reqfile->GetFileHash());
172 CPacket* packet = new CPacket(dataStartupLoadReq, OP_EDONKEYPROT, OP_STARTUPLOADREQ);
173 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
174 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_STARTUPLOADREQ to ") + GetFullIP());
175 SendPacket(packet, true, true);
179 bool CUpDownClient::IsSourceRequestAllowed()
181 //#warning REWRITE - Source swapping from eMule.
182 // 0.42e
183 uint32 dwTickCount = ::GetTickCount() + CONNECTION_LATENCY;
184 uint32 nTimePassedClient = dwTickCount - GetLastSrcAnswerTime();
185 uint32 nTimePassedFile = dwTickCount - m_reqfile->GetLastAnsweredTime();
186 bool bNeverAskedBefore = (GetLastAskedForSources() == 0);
188 uint32 uSources = m_reqfile->GetSourceCount();
189 return (
190 // if client has the correct extended protocol
191 ExtProtocolAvailable() && (SupportsSourceExchange2() || GetSourceExchange1Version() > 1) &&
192 // AND if we need more sources
193 thePrefs::GetMaxSourcePerFileSoft() > uSources &&
194 // AND if...
196 //source is not complete and file is very rare
197 ( !m_bCompleteSource
198 && (bNeverAskedBefore || nTimePassedClient > SOURCECLIENTREASKS)
199 && (uSources <= RARE_FILE/5)
200 ) ||
201 //source is not complete and file is rare
202 ( !m_bCompleteSource
203 && (bNeverAskedBefore || nTimePassedClient > SOURCECLIENTREASKS)
204 && (uSources <= RARE_FILE || uSources - m_reqfile->GetValidSourcesCount() <= RARE_FILE / 2)
205 && (nTimePassedFile > SOURCECLIENTREASKF)
206 ) ||
207 // OR if file is not rare
208 ( (bNeverAskedBefore || nTimePassedClient > (unsigned)(SOURCECLIENTREASKS * MINCOMMONPENALTY))
209 && (nTimePassedFile > (unsigned)(SOURCECLIENTREASKF * MINCOMMONPENALTY))
216 void CUpDownClient::SendFileRequest()
218 wxCHECK_RET(m_reqfile, wxT("Cannot request file when no reqfile is set"));
220 CMemFile dataFileReq(16+16);
221 dataFileReq.WriteHash(m_reqfile->GetFileHash());
223 if (SupportMultiPacket()) {
224 wxString sent_opcodes;
226 if (SupportExtMultiPacket()) {
227 dataFileReq.WriteUInt64(m_reqfile->GetFileSize());
230 AddDebugLogLineM(false, logClient, wxT("Sending file request to client"));
232 dataFileReq.WriteUInt8(OP_REQUESTFILENAME);
233 sent_opcodes += wxT("|RFNM|");
234 // Extended information
235 if (GetExtendedRequestsVersion() > 0) {
236 m_reqfile->WritePartStatus(&dataFileReq);
238 if (GetExtendedRequestsVersion() > 1) {
239 m_reqfile->WriteCompleteSourcesCount(&dataFileReq);
241 if (m_reqfile->GetPartCount() > 1) {
242 sent_opcodes += wxT("|RFID|");
243 dataFileReq.WriteUInt8(OP_SETREQFILEID);
245 if (IsEmuleClient()) {
246 SetRemoteQueueFull( true );
247 SetRemoteQueueRank(0);
249 if (IsSourceRequestAllowed()) {
250 if (SupportsSourceExchange2()){
251 sent_opcodes += wxT("|RSRC2|");
252 dataFileReq.WriteUInt8(OP_REQUESTSOURCES2);
253 dataFileReq.WriteUInt8(SOURCEEXCHANGE2_VERSION);
254 const uint16 nOptions = 0; // 16 ... Reserved
255 dataFileReq.WriteUInt16(nOptions);
256 } else{
257 sent_opcodes += wxT("|RSRC|");
258 dataFileReq.WriteUInt8(OP_REQUESTSOURCES);
260 m_reqfile->SetLastAnsweredTimeTimeout();
261 SetLastAskedForSources();
263 if (IsSupportingAICH()) {
264 sent_opcodes += wxT("|AFHR|");
265 dataFileReq.WriteUInt8(OP_AICHFILEHASHREQ);
267 CPacket* packet = new CPacket(dataFileReq, OP_EMULEPROT, (SupportExtMultiPacket() ? OP_MULTIPACKET_EXT : OP_MULTIPACKET));
268 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
269 AddDebugLogLineM( false, logLocalClient, wxString::Format(wxT("Local Client: %s "), (SupportExtMultiPacket() ? wxT("OP_MULTIPACKET_EXT (") : wxT("OP_MULTIPACKET (") )) + sent_opcodes + wxT(") to ") + GetFullIP());
270 SendPacket(packet, true);
271 } else {
272 //This is extended information
273 if (GetExtendedRequestsVersion() > 0 ) {
274 m_reqfile->WritePartStatus(&dataFileReq);
276 if (GetExtendedRequestsVersion() > 1 ) {
277 m_reqfile->WriteCompleteSourcesCount(&dataFileReq);
279 CPacket* packet = new CPacket(dataFileReq, OP_EDONKEYPROT, OP_REQUESTFILENAME);
280 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
281 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_REQUESTFILENAME to ") + GetFullIP() );
282 SendPacket(packet, true);
284 // 26-Jul-2003: removed requesting the file status for files <= PARTSIZE for better compatibility with ed2k protocol (eDonkeyHybrid).
285 // if the remote client answers the OP_REQUESTFILENAME with OP_REQFILENAMEANSWER the file is shared by the remote client. if we
286 // know that the file is shared, we know also that the file is complete and don't need to request the file status.
288 // Sending the packet could have deleted the client, check m_reqfile
289 if (m_reqfile && (m_reqfile->GetPartCount() > 1)) {
290 CMemFile dataSetReqFileID(16);
291 dataSetReqFileID.WriteHash(m_reqfile->GetFileHash());
292 packet = new CPacket(dataSetReqFileID, OP_EDONKEYPROT, OP_SETREQFILEID);
293 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
294 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_SETREQFILEID to ") + GetFullIP());
295 SendPacket(packet, true);
298 if (IsEmuleClient()) {
299 SetRemoteQueueFull( true );
300 SetRemoteQueueRank(0);
303 // Sending the packet could have deleted the client, check m_reqfile
304 if (m_reqfile && IsSourceRequestAllowed()) {
305 m_reqfile->SetLastAnsweredTimeTimeout();
307 CMemFile packetdata;
309 if (SupportsSourceExchange2()) {
310 packetdata.WriteUInt8(SOURCEEXCHANGE2_VERSION);
311 packetdata.WriteUInt16(0 /* Reserved */);
314 packetdata.WriteHash(m_reqfile->GetFileHash());
316 packet = new CPacket(packetdata, OP_EMULEPROT, SupportsSourceExchange2() ? OP_REQUESTSOURCES2 : OP_REQUESTSOURCES);
318 theStats::AddUpOverheadSourceExchange(packet->GetPacketSize());
319 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_REQUESTSOURCES to ") + GetFullIP() );
320 SendPacket(packet,true,true);
321 SetLastAskedForSources();
324 // Sending the packet could have deleted the client, check m_reqfile
325 if (m_reqfile && IsSupportingAICH()) {
326 packet = new CPacket(OP_AICHFILEHASHREQ,16,OP_EMULEPROT);
327 packet->Copy16ToDataBuffer((const char *)m_reqfile->GetFileHash().GetHash());
328 theStats::AddUpOverheadOther(packet->GetPacketSize());
329 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_AICHFILEHASHREQ to ") + GetFullIP());
330 SendPacket(packet,true,true);
336 void CUpDownClient::ProcessFileInfo(const CMemFile* data, const CPartFile* file)
338 // 0.42e
339 if (file==NULL) {
340 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; file==NULL)"));
342 if (m_reqfile==NULL) {
343 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; m_reqfile==NULL)"));
345 if (file != m_reqfile) {
346 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; m_reqfile!=file)"));
349 m_clientFilename = data->ReadString((GetUnicodeSupport() != utf8strNone));
351 // 26-Jul-2003: removed requesting the file status for files <= PARTSIZE for better compatibility with ed2k protocol (eDonkeyHybrid).
352 // if the remote client answers the OP_REQUESTFILENAME with OP_REQFILENAMEANSWER the file is shared by the remote client. if we
353 // know that the file is shared, we know also that the file is complete and don't need to request the file status.
354 if (m_reqfile->GetPartCount() == 1) {
355 m_nPartCount = m_reqfile->GetPartCount();
357 m_reqfile->UpdatePartsFrequency( this, false ); // Decrement
358 m_downPartStatus.setsize( m_nPartCount, 1 );
359 m_reqfile->UpdatePartsFrequency( this, true ); // Increment
361 m_bCompleteSource = true;
363 UpdateDisplayedInfo();
364 // even if the file is <= PARTSIZE, we _may_ need the hashset for that file (if the file size == PARTSIZE)
365 if (m_reqfile->IsHashSetNeeded()) {
366 if (m_socket) {
367 CPacket* packet = new CPacket(OP_HASHSETREQUEST,16, OP_EDONKEYPROT);
368 packet->Copy16ToDataBuffer((const char *)m_reqfile->GetFileHash().GetHash());
369 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
370 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_HASHSETREQUEST to ") + GetFullIP());
371 SendPacket(packet,true,true);
372 SetDownloadState(DS_REQHASHSET);
373 m_fHashsetRequesting = 1;
374 m_reqfile->SetHashSetNeeded(false);
375 } else {
376 wxFAIL;
378 } else {
379 SendStartupLoadReq();
381 m_reqfile->UpdatePartsInfo();
385 void CUpDownClient::ProcessFileStatus(bool bUdpPacket, const CMemFile* data, const CPartFile* file)
387 // 0.42e
388 wxString strReqFileNull(wxT("ERROR: Wrong file ID (ProcessFileStatus; m_reqfile==NULL)"));
390 if ( !m_reqfile || file != m_reqfile ){
391 if (!m_reqfile) {
392 throw strReqFileNull;
394 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileStatus; m_reqfile!=file)"));
397 uint16 nED2KPartCount = data->ReadUInt16();
399 m_reqfile->UpdatePartsFrequency( this, false ); // Decrement
400 m_downPartStatus.clear();
402 bool bPartsNeeded = false;
403 int iNeeded = 0;
404 if (!nED2KPartCount)
406 m_nPartCount = m_reqfile->GetPartCount();
407 m_downPartStatus.setsize( m_nPartCount, 1);
408 bPartsNeeded = true;
409 m_bCompleteSource = true;
411 else
413 // Somehow this happened.
414 if (!m_reqfile) {
415 throw strReqFileNull;
417 if (m_reqfile->GetED2KPartCount() != nED2KPartCount)
419 wxString strError;
420 strError << wxT("ProcessFileStatus - wrong part number recv=") << nED2KPartCount <<
421 wxT(" expected=") << m_reqfile->GetED2KPartCount() << wxT(" ") <<
422 m_reqfile->GetFileHash().Encode();
423 m_nPartCount = 0;
424 throw strError;
426 m_nPartCount = m_reqfile->GetPartCount();
428 m_bCompleteSource = false;
429 m_downPartStatus.setsize( m_nPartCount, 0 );
430 uint16 done = 0;
432 try {
433 while (done != m_nPartCount) {
434 uint8 toread = data->ReadUInt8();
436 for ( uint8 i = 0;i < 8; i++ ) {
437 bool status = ((toread>>i)&1)? 1:0;
438 m_downPartStatus.set(done, status);
440 if (status) {
441 if (!m_reqfile->IsComplete(done)){
442 bPartsNeeded = true;
443 iNeeded++;
446 done++;
447 if (done == m_nPartCount) {
448 break;
452 } catch( ... ) {
453 // We want the counts to be updated, even if we fail to read everything
454 m_reqfile->UpdatePartsFrequency( this, true ); // Increment
456 throw;
460 m_reqfile->UpdatePartsFrequency( this, true ); // Increment
462 UpdateDisplayedInfo();
464 // NOTE: This function is invoked from TCP and UDP socket!
465 if (!bUdpPacket) {
466 if (!bPartsNeeded) {
467 SetDownloadState(DS_NONEEDEDPARTS);
468 } else if (m_reqfile->IsHashSetNeeded()) {
469 //If we are using the eMule filerequest packets, this is taken care of in the Multipacket!
470 if (m_socket) {
471 CPacket* packet = new CPacket(OP_HASHSETREQUEST,16, OP_EDONKEYPROT);
472 packet->Copy16ToDataBuffer((const char *)m_reqfile->GetFileHash().GetHash());
473 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
474 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_HASHSETREQUEST to ") + GetFullIP());
475 SendPacket(packet, true, true);
476 SetDownloadState(DS_REQHASHSET);
477 m_fHashsetRequesting = 1;
478 m_reqfile->SetHashSetNeeded(false);
479 } else {
480 wxFAIL;
483 else {
484 SendStartupLoadReq();
487 else {
488 if (!bPartsNeeded) {
489 SetDownloadState(DS_NONEEDEDPARTS);
490 } else {
491 SetDownloadState(DS_ONQUEUE);
494 m_reqfile->UpdatePartsInfo();
497 bool CUpDownClient::AddRequestForAnotherFile(CPartFile* file)
499 if ( m_A4AF_list.find( file ) == m_A4AF_list.end() ) {
500 // When we access a non-existing entry entry, it will be zeroed by default,
501 // so we have to set NeededParts. All in one go.
502 m_A4AF_list[file].NeededParts = true;
503 file->AddA4AFSource( this );
504 return true;
505 } else {
506 return false;
510 bool CUpDownClient::DeleteFileRequest(CPartFile* file)
512 return (m_A4AF_list.erase( file ) > 0);
515 void CUpDownClient::DeleteAllFileRequests()
517 m_A4AF_list.clear();
521 /* eMule 0.30c implementation, i give it a try (Creteil) BEGIN ... */
522 void CUpDownClient::SetDownloadState(uint8 byNewState)
524 if (m_nDownloadState != byNewState) {
525 if (m_reqfile) {
526 // Notify the client that this source has changed its state
527 m_reqfile->ClientStateChanged( m_nDownloadState, byNewState );
529 if (byNewState == DS_DOWNLOADING) {
530 m_reqfile->AddDownloadingSource(this);
531 } else if (m_nDownloadState == DS_DOWNLOADING) {
532 m_reqfile->RemoveDownloadingSource(this);
535 if (byNewState == DS_DOWNLOADING) {
536 msReceivedPrev = GetTickCount();
537 theStats::AddDownloadingSource();
538 } else if (m_nDownloadState == DS_DOWNLOADING) {
539 theStats::RemoveDownloadingSource();
542 if (m_nDownloadState == DS_DOWNLOADING) {
543 m_nDownloadState = byNewState;
544 ClearDownloadBlockRequests();
546 kBpsDown = 0.0;
547 bytesReceivedCycle = 0;
548 msReceivedPrev = 0;
549 if (byNewState == DS_NONE) {
550 if (m_reqfile) {
551 m_reqfile->UpdatePartsFrequency( this, false ); // Decrement
553 m_downPartStatus.clear();
554 m_nPartCount = 0;
556 if (m_socket && byNewState != DS_ERROR) {
557 m_socket->DisableDownloadLimit();
560 m_nDownloadState = byNewState;
561 if(GetDownloadState() == DS_DOWNLOADING) {
562 if (IsEmuleClient()) {
563 SetRemoteQueueFull(false);
565 SetRemoteQueueRank(0); // eMule 0.30c set like this ...
567 UpdateDisplayedInfo(true);
570 /* eMule 0.30c implementation, i give it a try (Creteil) END ... */
572 void CUpDownClient::ProcessHashSet(const byte* packet, uint32 size)
574 if ((!m_reqfile) || md4cmp(packet,m_reqfile->GetFileHash().GetHash())) {
575 throw wxString(wxT("Wrong fileid sent (ProcessHashSet)"));
577 if (!m_fHashsetRequesting) {
578 throw wxString(wxT("Received unsolicited hashset, ignoring it."));
580 CMemFile data(packet,size);
581 if (m_reqfile->LoadHashsetFromFile(&data,true)) {
582 m_fHashsetRequesting = 0;
583 } else {
584 m_reqfile->SetHashSetNeeded(true);
585 throw wxString(wxT("Corrupted or invalid hashset received"));
587 SendStartupLoadReq();
590 void CUpDownClient::SendBlockRequests()
592 uint32 current_time = ::GetTickCount();
593 if (GetVBTTags()) {
595 // Ask new blocks only when all completed
596 if (m_PendingBlocks_list.size()) {
597 return;
600 if ((m_dwLastBlockReceived + SEC2MS(5)) > current_time) {
601 // We received last block in less than 5 secs? Let's request faster.
602 m_MaxBlockRequests = m_MaxBlockRequests << 1;
603 if ( m_MaxBlockRequests > 0x20) {
604 m_MaxBlockRequests = 0x20;
606 } else {
607 m_MaxBlockRequests = m_MaxBlockRequests >> 1;
608 if ( m_MaxBlockRequests < STANDARD_BLOCKS_REQUEST) {
609 m_MaxBlockRequests = STANDARD_BLOCKS_REQUEST;
614 m_dwLastBlockReceived = current_time;
616 if (!m_reqfile) {
617 return;
620 uint8 version = GetVBTTags() ? 2 : 1;
622 if (m_DownloadBlocks_list.empty()) {
623 // Barry - instead of getting 3, just get how many is needed
624 uint16 count = m_MaxBlockRequests - m_PendingBlocks_list.size();
625 std::vector<Requested_Block_Struct*> toadd;
626 if (m_reqfile->GetNextRequestedBlock(this, toadd, count)) {
627 for (int i = 0; i != count; i++) {
628 m_DownloadBlocks_list.push_back(toadd[i]);
633 // Barry - Why are unfinished blocks requested again, not just new ones?
635 while (m_PendingBlocks_list.size() < m_MaxBlockRequests && !m_DownloadBlocks_list.empty()) {
636 Pending_Block_Struct* pblock = new Pending_Block_Struct;
637 pblock->block = m_DownloadBlocks_list.front();
638 pblock->zStream = NULL;
639 pblock->totalUnzipped = 0;
640 pblock->fZStreamError = 0;
641 pblock->fRecovered = 0;
642 m_PendingBlocks_list.push_back(pblock);
643 m_DownloadBlocks_list.pop_front();
647 if (m_PendingBlocks_list.empty()) {
649 CUpDownClient* slower_client = NULL;
651 if (thePrefs::GetDropSlowSources()) {
652 slower_client = m_reqfile->GetSlowerDownloadingClient(m_lastaverage, this);
655 if (slower_client == NULL) {
656 slower_client = this;
659 if (!slower_client->GetSentCancelTransfer()) {
660 CPacket* packet = new CPacket(OP_CANCELTRANSFER, 0, OP_EDONKEYPROT);
661 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
662 if (slower_client != this) {
663 // printf("Dropped client %p to allow client %p to download\n",slower_client, this);
665 slower_client->ClearDownloadBlockRequests();
666 slower_client->SendPacket(packet,true,true);
667 slower_client->SetSentCancelTransfer(1);
670 slower_client->SetDownloadState(DS_NONEEDEDPARTS);
672 if (slower_client != this) {
673 // Re-request freed blocks.
674 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_CANCELTRANSFER (faster source eager to transfer) to ") + slower_client->GetFullIP() );
675 wxASSERT(m_DownloadBlocks_list.empty());
676 wxASSERT(m_PendingBlocks_list.empty());
677 uint16 count = m_MaxBlockRequests;
678 std::vector<Requested_Block_Struct*> toadd;
679 if (m_reqfile->GetNextRequestedBlock(this, toadd, count)) {
680 for (int i = 0; i != count; i++) {
681 Pending_Block_Struct* pblock = new Pending_Block_Struct;
682 pblock->block = toadd[i];
683 pblock->zStream = NULL;
684 pblock->totalUnzipped = 0;
685 pblock->fZStreamError = 0;
686 pblock->fRecovered = 0;
687 m_PendingBlocks_list.push_back(pblock);
689 } else {
690 // WTF, we just freed blocks.
691 wxFAIL;
692 return;
694 } else {
695 // Drop this one.
696 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_CANCELTRANSFER (no free blocks) to ") + GetFullIP() );
697 //#warning Kry - Would be nice to swap A4AF here.
698 return;
702 CPacket* packet = NULL;
704 switch (version) {
705 case 2: {
706 // ED2Kv2 packet...
707 // Most common scenario: hash + blocks to request + every one
708 // having 2 uint32 tags
710 uint8 nBlocks = m_PendingBlocks_list.size();
711 if (nBlocks > m_MaxBlockRequests) {
712 nBlocks = m_MaxBlockRequests;
715 CMemFile data(16 + 1 + nBlocks*((2+4)*2));
717 data.WriteHash(m_reqfile->GetFileHash());
719 data.WriteUInt8(nBlocks);
721 std::list<Pending_Block_Struct*>::iterator it = m_PendingBlocks_list.begin();
722 while (nBlocks) {
723 wxASSERT(it != m_PendingBlocks_list.end());
724 wxASSERT( (*it)->block->StartOffset <= (*it)->block->EndOffset );
725 (*it)->fZStreamError = 0;
726 (*it)->fRecovered = 0;
727 CTagVarInt(/*Noname*/0,(*it)->block->StartOffset).WriteTagToFile(&data);
728 CTagVarInt(/*Noname*/0,(*it)->block->EndOffset).WriteTagToFile(&data);
729 ++it;
730 nBlocks--;
733 packet = new CPacket(data, OP_ED2KV2HEADER, OP_REQUESTPARTS);
734 AddDebugLogLineM( false, logLocalClient, CFormat(wxT("Local Client ED2Kv2: OP_REQUESTPARTS(%i) to %s"))
735 % (m_PendingBlocks_list.size()<m_MaxBlockRequests ? m_PendingBlocks_list.size() : m_MaxBlockRequests) % GetFullIP() );
737 break;
739 case 1: {
740 wxASSERT(m_MaxBlockRequests == STANDARD_BLOCKS_REQUEST);
742 //#warning Kry - I dont specially like this approach, we iterate one time too many
744 bool bHasLongBlocks = false;
746 std::list<Pending_Block_Struct*>::iterator it = m_PendingBlocks_list.begin();
747 for (uint32 i = 0; i != m_MaxBlockRequests; i++){
748 if (it != m_PendingBlocks_list.end()) {
749 Pending_Block_Struct* pending = *it++;
750 wxASSERT( pending->block->StartOffset <= pending->block->EndOffset );
751 if (pending->block->StartOffset > 0xFFFFFFFF || pending->block->EndOffset > 0xFFFFFFFF){
752 bHasLongBlocks = true;
753 if (!SupportsLargeFiles()){
754 // Requesting a large block from a client that doesn't support large files?
755 wxFAIL;
756 if (!GetSentCancelTransfer()){
757 CPacket* cancel_packet = new CPacket(OP_CANCELTRANSFER, 0, OP_EDONKEYPROT);
758 theStats::AddUpOverheadFileRequest(cancel_packet->GetPacketSize());
759 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
760 SendPacket(cancel_packet,true,true);
761 SetSentCancelTransfer(1);
763 SetDownloadState(DS_ERROR);
765 break;
770 CMemFile data(16 /*Hash*/ + (m_MaxBlockRequests*(bHasLongBlocks ? 8 : 4) /* uint32/64 start*/) + (3*(bHasLongBlocks ? 8 : 4)/* uint32/64 end*/));
771 data.WriteHash(m_reqfile->GetFileHash());
773 it = m_PendingBlocks_list.begin();
774 for (uint32 i = 0; i != m_MaxBlockRequests; i++) {
775 if (it != m_PendingBlocks_list.end()) {
776 Pending_Block_Struct* pending = *it++;
777 wxASSERT( pending->block->StartOffset <= pending->block->EndOffset );
778 pending->fZStreamError = 0;
779 pending->fRecovered = 0;
780 if (bHasLongBlocks) {
781 data.WriteUInt64(pending->block->StartOffset);
782 } else {
783 data.WriteUInt32(pending->block->StartOffset);
785 } else {
786 if (bHasLongBlocks) {
787 data.WriteUInt64(0);
788 } else {
789 data.WriteUInt32(0);
794 it = m_PendingBlocks_list.begin();
795 for (uint32 i = 0; i != m_MaxBlockRequests; i++) {
796 if (it != m_PendingBlocks_list.end()) {
797 Requested_Block_Struct* block = (*it++)->block;
798 if (bHasLongBlocks) {
799 data.WriteUInt64(block->EndOffset+1);
800 } else {
801 data.WriteUInt32(block->EndOffset+1);
803 } else {
804 if (bHasLongBlocks) {
805 data.WriteUInt64(0);
806 } else {
807 data.WriteUInt32(0);
811 packet = new CPacket(data, (bHasLongBlocks ? OP_EMULEPROT : OP_EDONKEYPROT), (bHasLongBlocks ? (uint8)OP_REQUESTPARTS_I64 : (uint8)OP_REQUESTPARTS));
812 AddDebugLogLineM( false, logLocalClient, wxString::Format(wxT("Local Client: %s to "),(bHasLongBlocks ? wxT("OP_REQUESTPARTS_I64") : wxT("OP_REQUESTPARTS"))) + GetFullIP() );
813 break;
815 default:
816 wxFAIL;
819 if (packet) {
820 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
821 SendPacket(packet, true, true);
822 } else {
823 wxFAIL;
828 Barry - Originally this only wrote to disk when a full 180k block
829 had been received from a client, and only asked for data in
830 180k blocks.
832 This meant that on average 90k was lost for every connection
833 to a client data source. That is a lot of wasted data.
835 To reduce the lost data, packets are now written to a buffer
836 and flushed to disk regularly regardless of size downloaded.
838 This includes compressed packets.
840 Data is also requested only where gaps are, not in 180k blocks.
841 The requests will still not exceed 180k, but may be smaller to
842 fill a gap.
845 void CUpDownClient::ProcessBlockPacket(const byte* packet, uint32 size, bool packed, bool largeblocks)
847 // Ignore if no data required
848 if (!(GetDownloadState() == DS_DOWNLOADING || GetDownloadState() == DS_NONEEDEDPARTS)) {
849 return;
852 // This vars are defined here to be able to use them on the catch
853 int header_size = 16;
854 uint64 nStartPos = 0;
855 uint64 nEndPos = 0;
856 uint32 nBlockSize = 0;
857 uint32 lenUnzipped = 0;
859 // Update stats
860 m_dwLastBlockReceived = ::GetTickCount();
862 try {
864 // Read data from packet
865 const CMemFile data(packet, size);
867 // Check that this data is for the correct file
868 if ((!m_reqfile) || data.ReadHash() != m_reqfile->GetFileHash()) {
869 throw wxString(wxT("Wrong fileid sent (ProcessBlockPacket)"));
872 // Find the start & end positions, and size of this chunk of data
874 if (largeblocks) {
875 nStartPos = data.ReadUInt64();
876 header_size += 8;
877 } else {
878 nStartPos = data.ReadUInt32();
879 header_size += 4;
882 if (packed) {
883 nBlockSize = data.ReadUInt32();
884 header_size += 4;
885 nEndPos = nStartPos + (size - header_size);
886 } else {
887 if (largeblocks) {
888 nEndPos = data.ReadUInt64();
889 header_size += 8;
890 } else {
891 nEndPos = data.ReadUInt32();
892 header_size += 4;
896 // Check that packet size matches the declared data size + header size
897 if ( nEndPos == nStartPos || size != ((nEndPos - nStartPos) + header_size)) {
898 throw wxString(wxT("Corrupted or invalid DataBlock received (ProcessBlockPacket)"));
900 theStats::AddDownloadFromSoft(GetClientSoft(),size - header_size);
901 bytesReceivedCycle += size - header_size;
903 credits->AddDownloaded(size - header_size, GetIP(), theApp->CryptoAvailable());
905 // Move end back one, should be inclusive
906 nEndPos--;
908 // Loop through to find the reserved block that this is within
909 std::list<Pending_Block_Struct*>::iterator it = m_PendingBlocks_list.begin();
910 for (; it != m_PendingBlocks_list.end(); ++it) {
911 Pending_Block_Struct* cur_block = *it;
913 if ((cur_block->block->StartOffset <= nStartPos) && (cur_block->block->EndOffset >= nStartPos)) {
914 // Found reserved block
916 if (cur_block->block->StartOffset == nStartPos) {
917 // This block just started transfering. Set the start time.
918 m_last_block_start = ::GetTickCountFullRes();
921 if (cur_block->fZStreamError){
922 AddDebugLogLineM( false, logZLib,
923 CFormat(wxT("Ignoring %u bytes of block %u-%u because of erroneous zstream state for file: %s"))
924 % (size - header_size) % nStartPos % nEndPos % m_reqfile->GetFileName());
925 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
926 return;
929 // Remember this start pos, used to draw part downloading in list
930 m_lastDownloadingPart = nStartPos / PARTSIZE;
932 // Occasionally packets are duplicated, no point writing it twice
933 // This will be 0 in these cases, or the length written otherwise
934 uint32 lenWritten = 0;
936 // Handle differently depending on whether packed or not
937 if (!packed) {
938 // security sanitize check
939 if (nEndPos > cur_block->block->EndOffset) {
940 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()));
941 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
942 return;
944 // Write to disk (will be buffered in part file class)
945 lenWritten = m_reqfile->WriteToBuffer( size - header_size, (byte*)(packet + header_size), nStartPos, nEndPos, cur_block->block, this);
946 } else {
947 // Packed
948 wxASSERT( (long int)size > 0 );
949 // Create space to store unzipped data, the size is
950 // only an initial guess, will be resized in unzip()
951 // if not big enough
952 lenUnzipped = (size * 2);
953 // Don't get too big
954 if (lenUnzipped > (BLOCKSIZE + 300)) {
955 lenUnzipped = (BLOCKSIZE + 300);
957 byte *unzipped = new byte[lenUnzipped];
959 // Try to unzip the packet
960 int result = unzip(cur_block, (byte*)(packet + header_size), (size - header_size), &unzipped, &lenUnzipped);
962 // no block can be uncompressed to >2GB, 'lenUnzipped' is obviously erroneous.
963 if (result == Z_OK && ((int)lenUnzipped >= 0)) {
965 // Write any unzipped data to disk
966 if (lenUnzipped > 0) {
967 wxASSERT( (int)lenUnzipped > 0 );
969 // Use the current start and end positions for the uncompressed data
970 nStartPos = cur_block->block->StartOffset + cur_block->totalUnzipped - lenUnzipped;
971 nEndPos = cur_block->block->StartOffset + cur_block->totalUnzipped - 1;
973 if (nStartPos > cur_block->block->EndOffset || nEndPos > cur_block->block->EndOffset) {
974 AddDebugLogLineM( false, logZLib,
975 CFormat(wxT("Corrupted compressed packet for '%s' received (error 666)")) % m_reqfile->GetFileName());
976 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
977 } else {
978 // Write uncompressed data to file
979 lenWritten = m_reqfile->WriteToBuffer( size - header_size,
980 unzipped,
981 nStartPos,
982 nEndPos,
983 cur_block->block,
984 this);
987 } else {
988 wxString strZipError;
989 if (cur_block->zStream && cur_block->zStream->msg) {
990 strZipError = wxT(" - ") + wxString::FromAscii(cur_block->zStream->msg);
993 AddDebugLogLineM( false, logZLib,
994 CFormat(wxT("Corrupted compressed packet for '%s' received (error %i): %s"))
995 % m_reqfile->GetFileName() % result % strZipError);
997 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
999 // If we had an zstream error, there is no chance that we could recover from it nor that we
1000 // could use the current zstream (which is in error state) any longer.
1001 if (cur_block->zStream){
1002 inflateEnd(cur_block->zStream);
1003 delete cur_block->zStream;
1004 cur_block->zStream = NULL;
1007 // Although we can't further use the current zstream, there is no need to disconnect the sending
1008 // client because the next zstream (a series of 10K-blocks which build a 180K-block) could be
1009 // valid again. Just ignore all further blocks for the current zstream.
1010 cur_block->fZStreamError = 1;
1011 cur_block->totalUnzipped = 0; // bluecow's fix
1013 delete [] unzipped;
1015 // These checks only need to be done if any data was written
1016 if (lenWritten > 0) {
1017 m_nTransferredDown += lenWritten;
1019 // If finished reserved block
1020 if (nEndPos == cur_block->block->EndOffset) {
1022 // Save last average speed based on data and time.
1023 // This should do bytes/sec.
1024 uint32 average_time = (::GetTickCountFullRes() - m_last_block_start);
1026 // Avoid divide by 0.
1027 if (average_time == 0) {
1028 average_time++;
1031 m_lastaverage = ((cur_block->block->EndOffset - cur_block->block->StartOffset) * 1000) / average_time;
1033 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
1034 delete cur_block->block;
1035 // Not always allocated
1036 if (cur_block->zStream) {
1037 inflateEnd(cur_block->zStream);
1038 delete cur_block->zStream;
1040 delete cur_block;
1041 m_PendingBlocks_list.erase(it);
1043 // Request next block
1044 SendBlockRequests();
1047 // Stop looping and exit method
1048 return;
1051 } catch (const CEOFException& e) {
1052 wxString error = wxString(wxT("Error reading "));
1053 if (packed) error += CFormat(wxT("packed (LU: %i) largeblocks ")) % lenUnzipped;
1054 error += CFormat(wxT("data packet: RS: %i HS: %i SP: %i EP: %i BS: %i -> "))
1055 % size % header_size % nStartPos % nEndPos % nBlockSize;
1056 AddDebugLogLineM(true, logRemoteClient, error + e.what());
1057 return;
1061 int CUpDownClient::unzip(Pending_Block_Struct *block, byte *zipped, uint32 lenZipped, byte **unzipped, uint32 *lenUnzipped, int iRecursion)
1063 int err = Z_DATA_ERROR;
1065 // Save some typing
1066 z_stream *zS = block->zStream;
1068 // Is this the first time this block has been unzipped
1069 if (zS == NULL) {
1070 // Create stream
1071 block->zStream = new z_stream;
1072 zS = block->zStream;
1074 // Initialise stream values
1075 zS->zalloc = (alloc_func)0;
1076 zS->zfree = (free_func)0;
1077 zS->opaque = (voidpf)0;
1079 // Set output data streams, do this here to avoid overwriting on recursive calls
1080 zS->next_out = (*unzipped);
1081 zS->avail_out = (*lenUnzipped);
1083 // Initialise the z_stream
1084 err = inflateInit(zS);
1085 if (err != Z_OK) {
1086 return err;
1090 // Use whatever input is provided
1091 zS->next_in = zipped;
1092 zS->avail_in = lenZipped;
1094 // Only set the output if not being called recursively
1095 if (iRecursion == 0) {
1096 zS->next_out = (*unzipped);
1097 zS->avail_out = (*lenUnzipped);
1100 // Try to unzip the data
1101 err = inflate(zS, Z_SYNC_FLUSH);
1103 // Is zip finished reading all currently available input and writing
1104 // all generated output
1105 if (err == Z_STREAM_END) {
1106 // Finish up
1107 err = inflateEnd(zS);
1108 if (err != Z_OK) {
1109 return err;
1112 // Got a good result, set the size to the amount unzipped in this call
1113 // (including all recursive calls)
1114 (*lenUnzipped) = (zS->total_out - block->totalUnzipped);
1115 block->totalUnzipped = zS->total_out;
1116 } else if ((err == Z_OK) && (zS->avail_out == 0) && (zS->avail_in != 0)) {
1118 // Output array was not big enough,
1119 // call recursively until there is enough space
1121 // What size should we try next
1122 uint32 newLength = (*lenUnzipped) *= 2;
1123 if (newLength == 0) {
1124 newLength = lenZipped * 2;
1126 // Copy any data that was successfully unzipped to new array
1127 byte *temp = new byte[newLength];
1128 wxASSERT( zS->total_out - block->totalUnzipped <= newLength );
1129 memcpy(temp, (*unzipped), (zS->total_out - block->totalUnzipped));
1130 delete [] (*unzipped);
1131 (*unzipped) = temp;
1132 (*lenUnzipped) = newLength;
1134 // Position stream output to correct place in new array
1135 zS->next_out = (*unzipped) + (zS->total_out - block->totalUnzipped);
1136 zS->avail_out = (*lenUnzipped) - (zS->total_out - block->totalUnzipped);
1138 // Try again
1139 err = unzip(block, zS->next_in, zS->avail_in, unzipped, lenUnzipped, iRecursion + 1);
1140 } else if ((err == Z_OK) && (zS->avail_in == 0)) {
1141 // All available input has been processed, everything ok.
1142 // Set the size to the amount unzipped in this call
1143 // (including all recursive calls)
1144 (*lenUnzipped) = (zS->total_out - block->totalUnzipped);
1145 block->totalUnzipped = zS->total_out;
1146 } else {
1147 // Should not get here unless input data is corrupt
1148 wxString strZipError;
1150 if ( zS->msg ) {
1151 strZipError = wxString::Format(wxT(" %d '"), err) + wxString::FromAscii(zS->msg) + wxT("'");
1152 } else if (err != Z_OK) {
1153 strZipError = wxString::Format(wxT(" %d"), err);
1156 AddDebugLogLineM(false, logZLib,
1157 CFormat(wxT("Unexpected zip error %s in file '%s'"))
1158 % strZipError % (m_reqfile ? m_reqfile->GetFileName() : CPath(wxT("?"))));
1161 if (err != Z_OK) {
1162 (*lenUnzipped) = 0;
1165 return err;
1169 // Speed is now updated only when data was received, calculated as
1170 // (data received) / (time since last receiption)
1171 // and slightly filtered (10s average).
1172 // Result is quite precise now and makes the DownloadRateAdjust workaround obsolete.
1174 float CUpDownClient::CalculateKBpsDown()
1176 const float tAverage = 10.0;
1177 uint32 msCur = GetTickCount();
1179 if (bytesReceivedCycle) {
1180 float dt = (msCur - msReceivedPrev) / 1000.0; // time since last reception
1181 if (dt < 0.01) { // (safeguard against divide-by-zero)
1182 dt = 0.01f; // diff should be 100ms actually
1184 float kBpsDownCur = bytesReceivedCycle / 1024.0 / dt;
1185 if (dt >= tAverage) {
1186 kBpsDown = kBpsDownCur;
1187 } else {
1188 kBpsDown = (kBpsDown * (tAverage - dt) + kBpsDownCur * dt) / tAverage;
1190 //AddDebugLogLineM( false, logLocalClient, CFormat(wxT("CalculateKBpsDown %p kbps %.1f kbpsCur %.1f dt %.3f rcv %d "))
1191 // % this % kBpsDown % kBpsDownCur % dt % bytesReceivedCycle);
1192 bytesReceivedCycle = 0;
1193 msReceivedPrev = msCur;
1196 m_cShowDR++;
1197 if (m_cShowDR == 30){
1198 m_cShowDR = 0;
1199 UpdateDisplayedInfo();
1201 if (msCur - m_dwLastBlockReceived > DOWNLOADTIMEOUT) {
1202 if (!GetSentCancelTransfer()){
1203 CPacket* packet = new CPacket(OP_CANCELTRANSFER, 0, OP_EDONKEYPROT);
1204 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
1205 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
1206 SendPacket(packet,true,true);
1207 SetSentCancelTransfer(1);
1209 SetDownloadState(DS_ONQUEUE);
1212 return kBpsDown;
1215 uint16 CUpDownClient::GetAvailablePartCount() const
1217 uint16 result = 0;
1218 for (int i = 0;i != m_nPartCount;i++){
1219 if (IsPartAvailable(i))
1220 result++;
1222 return result;
1225 void CUpDownClient::SetRemoteQueueRank(uint16 nr)
1227 m_nOldRemoteQueueRank = m_nRemoteQueueRank;
1228 m_nRemoteQueueRank = nr;
1229 UpdateDisplayedInfo();
1232 void CUpDownClient::UDPReaskACK(uint16 nNewQR)
1234 // 0.42e
1235 m_bUDPPending = false;
1236 SetRemoteQueueRank(nNewQR);
1237 m_dwLastAskedTime = ::GetTickCount();
1240 void CUpDownClient::UDPReaskFNF()
1242 m_bUDPPending = false;
1244 // avoid premature deletion of 'this' client
1245 if (GetDownloadState() != DS_DOWNLOADING){
1246 if (m_reqfile) {
1247 m_reqfile->AddDeadSource(this);
1250 theApp->downloadqueue->RemoveSource(this);
1251 if (!m_socket) {
1252 if (Disconnected(wxT("UDPReaskFNF m_socket=NULL"))) {
1253 Safe_Delete();
1256 } else {
1257 AddDebugLogLineM( false, logRemoteClient, wxT("UDP ANSWER FNF : ") + GetUserName() + wxT(" - did not remove client because of current download state") );
1261 void CUpDownClient::UDPReaskForDownload()
1264 wxASSERT(m_reqfile);
1266 if(!m_reqfile || m_bUDPPending ) {
1267 return;
1270 //#warning We should implement the quality tests for udp reliability
1272 if( m_nTotalUDPPackets > 3 && ((float)(m_nFailedUDPPackets/m_nTotalUDPPackets) > .3)) {
1273 return;
1277 if (thePrefs::GetEffectiveUDPPort() == 0) {
1278 return;
1281 if (m_nUDPPort != 0 && !theApp->IsFirewalled() && !IsConnected()) {
1282 //don't use udp to ask for sources
1283 if(IsSourceRequestAllowed()) {
1284 return;
1287 m_bUDPPending = true;
1289 CMemFile data(128);
1290 data.WriteHash(m_reqfile->GetFileHash());
1292 if (GetUDPVersion() > 3) {
1293 if (m_reqfile->IsPartFile()) {
1294 ((CPartFile*)m_reqfile)->WritePartStatus(&data);
1296 else {
1297 data.WriteUInt16(0);
1301 if (GetUDPVersion() > 2) {
1302 data.WriteUInt16(m_reqfile->m_nCompleteSourcesCount);
1305 CPacket* response = new CPacket(data, OP_EMULEPROT, OP_REASKFILEPING);
1306 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket: send OP_REASKFILEPING") );
1307 theStats::AddUpOverheadFileRequest(response->GetPacketSize());
1308 theApp->clientudp->SendPacket(response,GetConnectIP(),GetUDPPort(), ShouldReceiveCryptUDPPackets(), GetUserHash().GetHash(), false, 0);
1309 } else if (HasLowID() && GetBuddyIP() && GetBuddyPort() && HasValidBuddyID()) {
1311 m_bUDPPending = true;
1313 CMemFile data(128);
1315 data.WriteHash(CMD4Hash(GetBuddyID()));
1316 data.WriteHash(m_reqfile->GetFileHash());
1318 if (GetUDPVersion() > 3) {
1319 if (m_reqfile->IsPartFile()) {
1320 ((CPartFile*)m_reqfile)->WritePartStatus(&data);
1321 } else {
1322 data.WriteUInt16(0);
1326 if (GetUDPVersion() > 2) {
1327 data.WriteUInt16(m_reqfile->m_nCompleteSourcesCount);
1330 CPacket* response = new CPacket(data, OP_EMULEPROT, OP_REASKCALLBACKUDP);
1331 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket: send OP_REASKCALLBACKUDP") );
1332 theStats::AddUpOverheadFileRequest(response->GetPacketSize());
1333 theApp->clientudp->SendPacket(response, GetBuddyIP(), GetBuddyPort(), false, NULL, true, 0 );
1338 // Get the next part that is requested
1339 uint16 CUpDownClient::GetNextRequestedPart() const
1341 uint16 part = 0xffff;
1343 std::list<Pending_Block_Struct*>::const_iterator it = m_PendingBlocks_list.begin();
1344 for (; it != m_PendingBlocks_list.end(); ++it) {
1345 part = (*it)->block->StartOffset / PARTSIZE;
1346 if (part != m_lastDownloadingPart) {
1347 break;
1351 return part;
1355 void CUpDownClient::UpdateDisplayedInfo(bool force)
1357 uint32 curTick = ::GetTickCount();
1358 if(force || curTick-m_lastRefreshedDLDisplay > MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE) {
1359 // Check if we actually need to notify of changes
1360 bool update = m_reqfile && m_reqfile->ShowSources();
1362 // Check A4AF files only if needed
1363 if ( !update ) {
1364 A4AFList::iterator it = m_A4AF_list.begin();
1365 for ( ; it != m_A4AF_list.end(); ++it ) {
1366 if ( it->first->ShowSources() ) {
1367 update = true;
1368 break;
1373 // And finnaly trigger an event if there's any reason
1374 if ( update ) {
1375 SourceItemType type = A4AF_SOURCE;
1376 switch (GetDownloadState()) {
1377 case DS_DOWNLOADING:
1378 case DS_ONQUEUE:
1379 // We will send A4AF, which will be checked.
1380 break;
1381 default:
1382 type = UNAVAILABLE_SOURCE;
1383 break;
1386 Notify_SourceCtrlUpdateSource(this, type );
1387 Notify_SharedCtrlRefreshClient(this, AVAILABLE_SOURCE);
1390 m_lastRefreshedDLDisplay = curTick;
1394 uint8 CUpDownClient::GetObfuscationStatus() const
1396 uint8 ret = OBST_UNDEFINED;
1397 if (thePrefs::IsClientCryptLayerSupported()) {
1398 if (SupportsCryptLayer()) {
1399 if ((RequestsCryptLayer() || thePrefs::IsClientCryptLayerRequested()) && HasObfuscatedConnectionBeenEstablished()) {
1400 ret = OBST_ENABLED;
1401 } else {
1402 ret = OBST_SUPPORTED;
1404 } else {
1405 ret = OBST_NOT_SUPPORTED;
1407 } else {
1408 ret = OBST_DISABLED;
1410 return ret;
1413 uint16 CUpDownClient::GetUploadQueueWaitingPosition() const
1415 return theApp->uploadqueue->GetWaitingPosition(this);
1418 // IgnoreNoNeeded = will switch to files of which this source has no needed parts (if no better fiels found)
1419 // ignoreSuspensions = ignore timelimit for A4Af jumping
1420 // bRemoveCompletely = do not readd the file which the source is swapped from to the A4AF lists (needed if deleting or stopping a file)
1421 // toFile = Try to swap to this partfile only
1423 bool CUpDownClient::SwapToAnotherFile(bool bIgnoreNoNeeded, bool ignoreSuspensions, bool bRemoveCompletely, CPartFile* toFile)
1425 // Fail if m_reqfile is invalid
1426 if ( m_reqfile == NULL ) {
1427 return false;
1430 // It would be stupid to swap away a downloading source
1431 if (GetDownloadState() == DS_DOWNLOADING) {
1432 return false;
1435 // The iterator of the final target
1436 A4AFList::iterator target = m_A4AF_list.end();
1438 // Do we want to swap to a specific file?
1439 if ( toFile != NULL ) {
1440 A4AFList::iterator it = m_A4AF_list.find( toFile );
1441 if ( it != m_A4AF_list.end() ) {
1443 // We force ignoring of noneeded flag and timestamps
1444 if ( IsValidSwapTarget( it, true, true ) ) {
1445 // Set the target
1446 target = it;
1449 } else {
1450 // We want highest priority possible, but need to start with
1451 // a value less than any other priority
1452 char priority = -1;
1454 A4AFList::iterator it = m_A4AF_list.begin();
1455 for ( ; it != m_A4AF_list.end(); ++it ) {
1456 if ( IsValidSwapTarget( it, bIgnoreNoNeeded, ignoreSuspensions ) ) {
1457 char cur_priority = it->first->GetDownPriority();
1459 // We would prefer to get files with needed parts, thus rate them higher.
1460 // However, this really only matters if bIgnoreNoNeeded is true.
1461 if ( it->second.NeededParts )
1462 cur_priority += 10;
1464 // Change target if the current file has a higher rate than the previous
1465 if ( cur_priority > priority ) {
1466 priority = cur_priority;
1468 // Set the new target
1469 target = it;
1471 // Break on the first High-priority file with needed parts
1472 if ( priority == PR_HIGH + 10 ) {
1473 break;
1480 // Try to swap if we found a valid target
1481 if ( target != m_A4AF_list.end() ) {
1483 // Sainity check, if reqfile doesn't own the source, then something
1484 // is wrong and the swap cannot proceed.
1485 if ( m_reqfile->DelSource( this ) ) {
1486 CPartFile* SwapTo = target->first;
1488 // remove this client from the A4AF list of our new m_reqfile
1489 if ( SwapTo->RemoveA4AFSource( this ) ) {
1490 Notify_SourceCtrlRemoveSource(this, SwapTo);
1493 m_reqfile->RemoveDownloadingSource( this );
1495 // Do we want to remove it completly? Say if the old file is getting deleted
1496 if ( !bRemoveCompletely ) {
1497 m_reqfile->AddA4AFSource( this );
1499 // Set the status of the old file
1500 m_A4AF_list[m_reqfile].NeededParts = (GetDownloadState() != DS_NONEEDEDPARTS);
1502 // Avoid swapping to this file for a while
1503 m_A4AF_list[m_reqfile].timestamp = ::GetTickCount();
1505 Notify_SourceCtrlAddSource(m_reqfile, this, A4AF_SOURCE);
1506 } else {
1507 Notify_SourceCtrlRemoveSource( this, m_reqfile );
1510 SetDownloadState(DS_NONE);
1511 ResetFileStatusInfo();
1513 m_nRemoteQueueRank = 0;
1514 m_nOldRemoteQueueRank = 0;
1516 m_reqfile->UpdatePartsInfo();
1518 SetRequestFile( SwapTo );
1520 SwapTo->AddSource( this );
1522 Notify_SourceCtrlAddSource(SwapTo, this, UNAVAILABLE_SOURCE);
1524 // Remove the new reqfile from the list of other files
1525 m_A4AF_list.erase( target );
1527 return true;
1531 return false;
1535 bool CUpDownClient::IsValidSwapTarget( A4AFList::iterator it, bool ignorenoneeded, bool ignoresuspended )
1537 wxASSERT( it != m_A4AF_list.end() && it->first );
1539 // Check if this file has been suspended
1540 if ( !ignoresuspended ) {
1541 if ( ::GetTickCount() - it->second.timestamp >= PURGESOURCESWAPSTOP ) {
1542 // The wait-time has been exceeded and the file is now a valid target
1543 it->second.timestamp = 0;
1544 } else {
1545 // The file was still suspended and we are not ignoring suspensions
1546 return false;
1550 // Check if the client has needed parts
1551 if ( !ignorenoneeded ) {
1552 if ( !it->second.NeededParts ) {
1553 return false;
1557 // Final checks to see if the client is a valid target
1558 CPartFile* cur_file = it->first;
1559 if ( ( cur_file != m_reqfile && !cur_file->IsStopped() ) &&
1560 ( cur_file->GetStatus() == PS_READY || cur_file->GetStatus() == PS_EMPTY ) &&
1561 ( cur_file->IsPartFile() ) )
1563 return true;
1564 } else {
1565 return false;
1570 void CUpDownClient::SetRequestFile(CPartFile* reqfile)
1572 if ( m_reqfile != reqfile ) {
1573 // Decrement the source-count of the old request-file
1574 if ( m_reqfile ) {
1575 m_reqfile->ClientStateChanged( GetDownloadState(), -1 );
1576 m_reqfile->UpdatePartsFrequency( this, false );
1579 m_nPartCount = 0;
1580 m_downPartStatus.clear();
1582 m_reqfile = reqfile;
1584 if ( reqfile ) {
1585 // Increment the source-count of the new request-file
1586 m_reqfile->ClientStateChanged( -1, GetDownloadState() );
1588 m_nPartCount = reqfile->GetPartCount();
1593 void CUpDownClient::SetReqFileAICHHash(CAICHHash* val){
1594 if(m_pReqFileAICHHash != NULL && m_pReqFileAICHHash != val)
1595 delete m_pReqFileAICHHash;
1596 m_pReqFileAICHHash = val;
1599 void CUpDownClient::SendAICHRequest(CPartFile* pForFile, uint16 nPart){
1600 CAICHRequestedData request;
1601 request.m_nPart = nPart;
1602 request.m_pClient = this;
1603 request.m_pPartFile = pForFile;
1604 CAICHHashSet::m_liRequestedData.push_back(request);
1605 m_fAICHRequested = TRUE;
1606 CMemFile data;
1607 data.WriteHash(pForFile->GetFileHash());
1608 data.WriteUInt16(nPart);
1609 pForFile->GetAICHHashset()->GetMasterHash().Write(&data);
1610 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_AICHREQUEST);
1611 theStats::AddUpOverheadOther(packet->GetPacketSize());
1612 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_AICHREQUEST to") + GetFullIP());
1613 SafeSendPacket(packet);
1616 void CUpDownClient::ProcessAICHAnswer(const byte* packet, uint32 size)
1618 if (m_fAICHRequested == FALSE){
1619 throw wxString(wxT("Received unrequested AICH Packet"));
1621 m_fAICHRequested = FALSE;
1623 CMemFile data(packet, size);
1624 if (size <= 16){
1625 CAICHHashSet::ClientAICHRequestFailed(this);
1626 return;
1629 CMD4Hash hash = data.ReadHash();
1630 CPartFile* pPartFile = theApp->downloadqueue->GetFileByID(hash);
1631 CAICHRequestedData request = CAICHHashSet::GetAICHReqDetails(this);
1632 uint16 nPart = data.ReadUInt16();
1633 if (pPartFile != NULL && request.m_pPartFile == pPartFile && request.m_pClient == this && nPart == request.m_nPart){
1634 CAICHHash ahMasterHash(&data);
1635 if ( (pPartFile->GetAICHHashset()->GetStatus() == AICH_TRUSTED || pPartFile->GetAICHHashset()->GetStatus() == AICH_VERIFIED)
1636 && ahMasterHash == pPartFile->GetAICHHashset()->GetMasterHash())
1638 if(pPartFile->GetAICHHashset()->ReadRecoveryData(request.m_nPart*PARTSIZE, &data)){
1639 // finally all checks passed, everythings seem to be fine
1640 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1641 CAICHHashSet::RemoveClientAICHRequest(this);
1642 pPartFile->AICHRecoveryDataAvailable(request.m_nPart);
1643 return;
1644 } else {
1645 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1647 } else {
1648 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Answer: Masterhash differs from packethash or hashset has no trusted Masterhash") );
1650 } else {
1651 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Answer: requested values differ from values in packet") );
1654 CAICHHashSet::ClientAICHRequestFailed(this);
1658 void CUpDownClient::ProcessAICHRequest(const byte* packet, uint32 size)
1660 if (size != 16 + 2 + CAICHHash::GetHashSize()) {
1661 throw wxString(wxT("Received AICH Request Packet with wrong size"));
1664 CMemFile data(packet, size);
1666 CMD4Hash hash = data.ReadHash();
1667 uint16 nPart = data.ReadUInt16();
1668 CAICHHash ahMasterHash(&data);
1669 CKnownFile* pKnownFile = theApp->sharedfiles->GetFileByID(hash);
1670 if (pKnownFile != NULL){
1671 if (pKnownFile->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE && pKnownFile->GetAICHHashset()->HasValidMasterHash()
1672 && pKnownFile->GetAICHHashset()->GetMasterHash() == ahMasterHash && pKnownFile->GetPartCount() > nPart
1673 && pKnownFile->GetFileSize() > EMBLOCKSIZE && pKnownFile->GetFileSize() - PARTSIZE*nPart > EMBLOCKSIZE)
1675 CMemFile fileResponse;
1676 fileResponse.WriteHash(pKnownFile->GetFileHash());
1677 fileResponse.WriteUInt16(nPart);
1678 pKnownFile->GetAICHHashset()->GetMasterHash().Write(&fileResponse);
1679 if (pKnownFile->GetAICHHashset()->CreatePartRecoveryData(nPart*PARTSIZE, &fileResponse)){
1680 AddDebugLogLineM(false, logAICHTransfer,
1681 CFormat(wxT("AICH Packet Request: Sucessfully created and send recoverydata for '%s' to %s"))
1682 % pKnownFile->GetFileName() % GetClientFullInfo());
1684 CPacket* packAnswer = new CPacket(fileResponse, OP_EMULEPROT, OP_AICHANSWER);
1685 theStats::AddUpOverheadOther(packAnswer->GetPacketSize());
1686 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1687 SafeSendPacket(packAnswer);
1688 return;
1689 } else {
1690 AddDebugLogLineM(false, logAICHTransfer,
1691 CFormat(wxT("AICH Packet Request: Failed to create recoverydata for '%s' to %s"))
1692 % pKnownFile->GetFileName() % GetClientFullInfo());
1694 } else {
1695 AddDebugLogLineM(false, logAICHTransfer,
1696 CFormat(wxT("AICH Packet Request: Failed to create recoverydata - Hashset not ready or requested Hash differs from Masterhash for '%s' to %s"))
1697 % pKnownFile->GetFileName() % GetClientFullInfo());
1699 } else {
1700 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Request: Failed to find requested shared file - ") + GetClientFullInfo() );
1703 CPacket* packAnswer = new CPacket(OP_AICHANSWER, 16, OP_EMULEPROT);
1704 packAnswer->Copy16ToDataBuffer(hash.GetHash());
1705 theStats::AddUpOverheadOther(packAnswer->GetPacketSize());
1706 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1707 SafeSendPacket(packAnswer);
1710 void CUpDownClient::ProcessAICHFileHash(CMemFile* data, const CPartFile* file){
1711 CPartFile* pPartFile;
1712 if (file == NULL){
1713 pPartFile = theApp->downloadqueue->GetFileByID(data->ReadHash());
1714 } else {
1715 pPartFile = (CPartFile*)file;
1717 CAICHHash ahMasterHash(data);
1719 if(pPartFile != NULL && pPartFile == GetRequestFile()){
1720 SetReqFileAICHHash(new CAICHHash(ahMasterHash));
1721 pPartFile->GetAICHHashset()->UntrustedHashReceived(ahMasterHash, GetConnectIP());
1722 } else {
1723 AddDebugLogLineM( false, logAICHTransfer, wxT("ProcessAICHFileHash(): PartFile not found or Partfile differs from requested file, ") + GetClientFullInfo() );
1726 // File_checked_for_headers