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