Upstream tarball 9861
[amule.git] / src / DownloadClient.cpp
blob462006e8f290380a8772baee2648fa62bc39acbb
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_*
54 #ifdef __MULE_UNUSED_CODE__
55 // This function is left as a reminder.
56 // Changes here _must_ be reflected in CClientList::FindMatchingClient.
57 bool CUpDownClient::Compare(const CUpDownClient* tocomp, bool bIgnoreUserhash) const
59 if (!tocomp) {
60 // should we wxASSERT here?
61 return false;
64 //Compare only the user hash..
65 if(!bIgnoreUserhash && HasValidHash() && tocomp->HasValidHash()) {
66 return GetUserHash() == tocomp->GetUserHash();
69 if (HasLowID()) {
70 //User is firewalled.. Must do two checks..
71 if (GetIP()!=0 && GetIP() == tocomp->GetIP()) {
72 //The IP of both match
73 if (GetUserPort()!=0 && GetUserPort() == tocomp->GetUserPort()) {
74 //IP-UserPort matches
75 return true;
77 if (GetKadPort()!=0 && GetKadPort() == tocomp->GetKadPort()) {
78 //IP-KadPort Matches
79 return true;
83 if (GetUserIDHybrid()!=0
84 && GetUserIDHybrid() == tocomp->GetUserIDHybrid()
85 && GetServerIP()!=0
86 && GetServerIP() == tocomp->GetServerIP()
87 && GetServerPort()!=0
88 && GetServerPort() == tocomp->GetServerPort()) {
89 //Both have the same lowID, Same serverIP and Port..
90 return true;
93 //Both IP, and Server do not match..
94 return false;
97 //User is not firewalled.
98 if (GetUserPort()!=0) {
99 //User has a Port, lets check the rest.
100 if (GetIP() != 0 && tocomp->GetIP() != 0) {
101 //Both clients have a verified IP..
102 if(GetIP() == tocomp->GetIP() && GetUserPort() == tocomp->GetUserPort()) {
103 //IP and UserPort match..
104 return true;
106 } else {
107 //One of the two clients do not have a verified IP
108 if (GetUserIDHybrid() == tocomp->GetUserIDHybrid() && GetUserPort() == tocomp->GetUserPort()) {
109 //ID and Port Match..
110 return true;
115 if(GetKadPort()!=0) {
116 //User has a Kad Port.
117 if(GetIP() != 0 && tocomp->GetIP() != 0) {
118 //Both clients have a verified IP.
119 if(GetIP() == tocomp->GetIP() && GetKadPort() == tocomp->GetKadPort()) {
120 //IP and KadPort Match..
121 return true;
123 } else {
124 //One of the users do not have a verified IP.
125 if (GetUserIDHybrid() == tocomp->GetUserIDHybrid() && GetKadPort() == tocomp->GetKadPort()) {
126 //ID and KadProt Match..
127 return true;
132 //No Matches..
133 return false;
135 #endif
138 bool CUpDownClient::AskForDownload()
140 // 0.42e
141 if (theApp->listensocket->TooManySockets()) {
142 if (!m_socket) {
143 if (GetDownloadState() != DS_TOOMANYCONNS) {
144 SetDownloadState(DS_TOOMANYCONNS);
146 return true;
147 } else if (!m_socket->IsConnected()) {
148 if (GetDownloadState() != DS_TOOMANYCONNS) {
149 SetDownloadState(DS_TOOMANYCONNS);
151 return true;
154 m_bUDPPending = false;
155 m_dwLastAskedTime = ::GetTickCount();
156 SetDownloadState(DS_CONNECTING);
157 return TryToConnect();
161 void CUpDownClient::SendStartupLoadReq()
163 // 0.42e
164 if (m_socket==NULL || m_reqfile==NULL) {
165 return;
167 SetDownloadState(DS_ONQUEUE);
168 CMemFile dataStartupLoadReq(16);
169 dataStartupLoadReq.WriteHash(m_reqfile->GetFileHash());
170 CPacket* packet = new CPacket(dataStartupLoadReq, OP_EDONKEYPROT, OP_STARTUPLOADREQ);
171 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
172 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_STARTUPLOADREQ to ") + GetFullIP());
173 SendPacket(packet, true, true);
177 bool CUpDownClient::IsSourceRequestAllowed()
179 //#warning REWRITE - Source swapping from eMule.
180 // 0.42e
181 uint32 dwTickCount = ::GetTickCount() + CONNECTION_LATENCY;
182 uint32 nTimePassedClient = dwTickCount - GetLastSrcAnswerTime();
183 uint32 nTimePassedFile = dwTickCount - m_reqfile->GetLastAnsweredTime();
184 bool bNeverAskedBefore = (GetLastAskedForSources() == 0);
186 uint32 uSources = m_reqfile->GetSourceCount();
187 return (
188 // if client has the correct extended protocol
189 ExtProtocolAvailable() && (SupportsSourceExchange2() || GetSourceExchange1Version() > 1) &&
190 // AND if we need more sources
191 thePrefs::GetMaxSourcePerFileSoft() > uSources &&
192 // AND if...
194 //source is not complete and file is very rare
195 ( !m_bCompleteSource
196 && (bNeverAskedBefore || nTimePassedClient > SOURCECLIENTREASKS)
197 && (uSources <= RARE_FILE/5)
198 ) ||
199 //source is not complete and file is rare
200 ( !m_bCompleteSource
201 && (bNeverAskedBefore || nTimePassedClient > SOURCECLIENTREASKS)
202 && (uSources <= RARE_FILE || uSources - m_reqfile->GetValidSourcesCount() <= RARE_FILE / 2)
203 && (nTimePassedFile > SOURCECLIENTREASKF)
204 ) ||
205 // OR if file is not rare
206 ( (bNeverAskedBefore || nTimePassedClient > (unsigned)(SOURCECLIENTREASKS * MINCOMMONPENALTY))
207 && (nTimePassedFile > (unsigned)(SOURCECLIENTREASKF * MINCOMMONPENALTY))
214 void CUpDownClient::SendFileRequest()
216 wxCHECK_RET(m_reqfile, wxT("Cannot request file when no reqfile is set"));
218 CMemFile dataFileReq(16+16);
219 dataFileReq.WriteHash(m_reqfile->GetFileHash());
221 if (SupportMultiPacket()) {
222 wxString sent_opcodes;
224 if (SupportExtMultiPacket()) {
225 dataFileReq.WriteUInt64(m_reqfile->GetFileSize());
228 AddDebugLogLineM(false, logClient, wxT("Sending file request to client"));
230 dataFileReq.WriteUInt8(OP_REQUESTFILENAME);
231 sent_opcodes += wxT("|RFNM|");
232 // Extended information
233 if (GetExtendedRequestsVersion() > 0) {
234 m_reqfile->WritePartStatus(&dataFileReq);
236 if (GetExtendedRequestsVersion() > 1) {
237 m_reqfile->WriteCompleteSourcesCount(&dataFileReq);
239 if (m_reqfile->GetPartCount() > 1) {
240 sent_opcodes += wxT("|RFID|");
241 dataFileReq.WriteUInt8(OP_SETREQFILEID);
243 if (IsEmuleClient()) {
244 SetRemoteQueueFull( true );
245 SetRemoteQueueRank(0);
247 if (IsSourceRequestAllowed()) {
248 if (SupportsSourceExchange2()){
249 sent_opcodes += wxT("|RSRC2|");
250 dataFileReq.WriteUInt8(OP_REQUESTSOURCES2);
251 dataFileReq.WriteUInt8(SOURCEEXCHANGE2_VERSION);
252 const uint16 nOptions = 0; // 16 ... Reserved
253 dataFileReq.WriteUInt16(nOptions);
254 } else{
255 sent_opcodes += wxT("|RSRC|");
256 dataFileReq.WriteUInt8(OP_REQUESTSOURCES);
258 m_reqfile->SetLastAnsweredTimeTimeout();
259 SetLastAskedForSources();
261 if (IsSupportingAICH()) {
262 sent_opcodes += wxT("|AFHR|");
263 dataFileReq.WriteUInt8(OP_AICHFILEHASHREQ);
265 CPacket* packet = new CPacket(dataFileReq, OP_EMULEPROT, (SupportExtMultiPacket() ? OP_MULTIPACKET_EXT : OP_MULTIPACKET));
266 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
267 AddDebugLogLineM( false, logLocalClient, wxString::Format(wxT("Local Client: %s "), (SupportExtMultiPacket() ? wxT("OP_MULTIPACKET_EXT (") : wxT("OP_MULTIPACKET (") )) + sent_opcodes + wxT(") to ") + GetFullIP());
268 SendPacket(packet, true);
269 } else {
270 //This is extended information
271 if (GetExtendedRequestsVersion() > 0 ) {
272 m_reqfile->WritePartStatus(&dataFileReq);
274 if (GetExtendedRequestsVersion() > 1 ) {
275 m_reqfile->WriteCompleteSourcesCount(&dataFileReq);
277 CPacket* packet = new CPacket(dataFileReq, OP_EDONKEYPROT, OP_REQUESTFILENAME);
278 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
279 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_REQUESTFILENAME to ") + GetFullIP() );
280 SendPacket(packet, true);
282 // 26-Jul-2003: removed requesting the file status for files <= PARTSIZE for better compatibility with ed2k protocol (eDonkeyHybrid).
283 // if the remote client answers the OP_REQUESTFILENAME with OP_REQFILENAMEANSWER the file is shared by the remote client. if we
284 // know that the file is shared, we know also that the file is complete and don't need to request the file status.
286 // Sending the packet could have deleted the client, check m_reqfile
287 if (m_reqfile && (m_reqfile->GetPartCount() > 1)) {
288 CMemFile dataSetReqFileID(16);
289 dataSetReqFileID.WriteHash(m_reqfile->GetFileHash());
290 packet = new CPacket(dataSetReqFileID, OP_EDONKEYPROT, OP_SETREQFILEID);
291 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
292 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_SETREQFILEID to ") + GetFullIP());
293 SendPacket(packet, true);
296 if (IsEmuleClient()) {
297 SetRemoteQueueFull( true );
298 SetRemoteQueueRank(0);
301 // Sending the packet could have deleted the client, check m_reqfile
302 if (m_reqfile && IsSourceRequestAllowed()) {
303 m_reqfile->SetLastAnsweredTimeTimeout();
305 CMemFile packetdata;
307 if (SupportsSourceExchange2()) {
308 packetdata.WriteUInt8(SOURCEEXCHANGE2_VERSION);
309 packetdata.WriteUInt16(0 /* Reserved */);
312 packetdata.WriteHash(m_reqfile->GetFileHash());
314 packet = new CPacket(packetdata, OP_EMULEPROT, SupportsSourceExchange2() ? OP_REQUESTSOURCES2 : OP_REQUESTSOURCES);
316 theStats::AddUpOverheadSourceExchange(packet->GetPacketSize());
317 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_REQUESTSOURCES to ") + GetFullIP() );
318 SendPacket(packet,true,true);
319 SetLastAskedForSources();
322 // Sending the packet could have deleted the client, check m_reqfile
323 if (m_reqfile && IsSupportingAICH()) {
324 packet = new CPacket(OP_AICHFILEHASHREQ,16,OP_EMULEPROT);
325 packet->Copy16ToDataBuffer((const char *)m_reqfile->GetFileHash().GetHash());
326 theStats::AddUpOverheadOther(packet->GetPacketSize());
327 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_AICHFILEHASHREQ to ") + GetFullIP());
328 SendPacket(packet,true,true);
334 void CUpDownClient::ProcessFileInfo(const CMemFile* data, const CPartFile* file)
336 // 0.42e
337 if (file==NULL) {
338 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; file==NULL)"));
340 if (m_reqfile==NULL) {
341 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; m_reqfile==NULL)"));
343 if (file != m_reqfile) {
344 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; m_reqfile!=file)"));
347 m_clientFilename = data->ReadString((GetUnicodeSupport() != utf8strNone));
349 // 26-Jul-2003: removed requesting the file status for files <= PARTSIZE for better compatibility with ed2k protocol (eDonkeyHybrid).
350 // if the remote client answers the OP_REQUESTFILENAME with OP_REQFILENAMEANSWER the file is shared by the remote client. if we
351 // know that the file is shared, we know also that the file is complete and don't need to request the file status.
352 if (m_reqfile->GetPartCount() == 1) {
353 m_nPartCount = m_reqfile->GetPartCount();
355 m_reqfile->UpdatePartsFrequency( this, false ); // Decrement
356 m_downPartStatus.clear();
357 m_downPartStatus.resize( m_nPartCount, 1 );
358 m_reqfile->UpdatePartsFrequency( this, true ); // Increment
360 m_bCompleteSource = true;
362 UpdateDisplayedInfo();
363 // even if the file is <= PARTSIZE, we _may_ need the hashset for that file (if the file size == PARTSIZE)
364 if (m_reqfile->IsHashSetNeeded()) {
365 if (m_socket) {
366 CPacket* packet = new CPacket(OP_HASHSETREQUEST,16, OP_EDONKEYPROT);
367 packet->Copy16ToDataBuffer((const char *)m_reqfile->GetFileHash().GetHash());
368 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
369 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_HASHSETREQUEST to ") + GetFullIP());
370 SendPacket(packet,true,true);
371 SetDownloadState(DS_REQHASHSET);
372 m_fHashsetRequesting = 1;
373 m_reqfile->SetHashSetNeeded(false);
374 } else {
375 wxFAIL;
377 } else {
378 SendStartupLoadReq();
380 m_reqfile->UpdatePartsInfo();
384 void CUpDownClient::ProcessFileStatus(bool bUdpPacket, const CMemFile* data, const CPartFile* file)
386 // 0.42e
387 wxString strReqFileNull(wxT("ERROR: Wrong file ID (ProcessFileStatus; m_reqfile==NULL)"));
389 if ( !m_reqfile || file != m_reqfile ){
390 if (!m_reqfile) {
391 throw strReqFileNull;
393 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileStatus; m_reqfile!=file)"));
396 uint16 nED2KPartCount = data->ReadUInt16();
398 m_reqfile->UpdatePartsFrequency( this, false ); // Decrement
399 m_downPartStatus.clear();
401 bool bPartsNeeded = false;
402 int iNeeded = 0;
403 if (!nED2KPartCount)
405 m_nPartCount = m_reqfile->GetPartCount();
406 m_downPartStatus.resize( m_nPartCount, 1);
407 bPartsNeeded = true;
408 m_bCompleteSource = true;
410 else
412 // Somehow this happened.
413 if (!m_reqfile) {
414 throw strReqFileNull;
416 if (m_reqfile->GetED2KPartCount() != nED2KPartCount)
418 wxString strError;
419 strError << wxT("ProcessFileStatus - wrong part number recv=") << nED2KPartCount <<
420 wxT(" expected=") << m_reqfile->GetED2KPartCount() << wxT(" ") <<
421 m_reqfile->GetFileHash().Encode();
422 m_nPartCount = 0;
423 throw strError;
425 m_nPartCount = m_reqfile->GetPartCount();
427 m_bCompleteSource = false;
428 m_downPartStatus.resize( m_nPartCount, 0 );
429 uint16 done = 0;
431 try {
432 while (done != m_nPartCount) {
433 uint8 toread = data->ReadUInt8();
435 for ( uint8 i = 0;i < 8; i++ ) {
436 m_downPartStatus[done] = ((toread>>i)&1)? 1:0;
438 if ( m_downPartStatus[done] ) {
439 if (!m_reqfile->IsComplete(done)){
440 bPartsNeeded = true;
441 iNeeded++;
444 done++;
445 if (done == m_nPartCount) {
446 break;
450 } catch( ... ) {
451 // We want the counts to be updated, even if we fail to read everything
452 m_reqfile->UpdatePartsFrequency( this, true ); // Increment
454 throw;
458 m_reqfile->UpdatePartsFrequency( this, true ); // Increment
460 UpdateDisplayedInfo();
462 // NOTE: This function is invoked from TCP and UDP socket!
463 if (!bUdpPacket) {
464 if (!bPartsNeeded) {
465 SetDownloadState(DS_NONEEDEDPARTS);
466 } else if (m_reqfile->IsHashSetNeeded()) {
467 //If we are using the eMule filerequest packets, this is taken care of in the Multipacket!
468 if (m_socket) {
469 CPacket* packet = new CPacket(OP_HASHSETREQUEST,16, OP_EDONKEYPROT);
470 packet->Copy16ToDataBuffer((const char *)m_reqfile->GetFileHash().GetHash());
471 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
472 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_HASHSETREQUEST to ") + GetFullIP());
473 SendPacket(packet, true, true);
474 SetDownloadState(DS_REQHASHSET);
475 m_fHashsetRequesting = 1;
476 m_reqfile->SetHashSetNeeded(false);
477 } else {
478 wxFAIL;
481 else {
482 SendStartupLoadReq();
485 else {
486 if (!bPartsNeeded) {
487 SetDownloadState(DS_NONEEDEDPARTS);
488 } else {
489 SetDownloadState(DS_ONQUEUE);
492 m_reqfile->UpdatePartsInfo();
495 bool CUpDownClient::AddRequestForAnotherFile(CPartFile* file)
497 if ( m_A4AF_list.find( file ) == m_A4AF_list.end() ) {
498 // When we access a non-existing entry entry, it will be zeroed by default,
499 // so we have to set NeededParts. All in one go.
500 m_A4AF_list[file].NeededParts = true;
501 file->AddA4AFSource( this );
502 return true;
503 } else {
504 return false;
508 bool CUpDownClient::DeleteFileRequest(CPartFile* file)
510 return (m_A4AF_list.erase( file ) > 0);
513 void CUpDownClient::DeleteAllFileRequests()
515 m_A4AF_list.clear();
519 /* eMule 0.30c implementation, i give it a try (Creteil) BEGIN ... */
520 void CUpDownClient::SetDownloadState(uint8 byNewState)
522 if (m_nDownloadState != byNewState) {
523 if (m_reqfile) {
524 // Notify the client that this source has changed its state
525 m_reqfile->ClientStateChanged( m_nDownloadState, byNewState );
527 if (byNewState == DS_DOWNLOADING) {
528 m_reqfile->AddDownloadingSource(this);
529 } else if (m_nDownloadState == DS_DOWNLOADING) {
530 m_reqfile->RemoveDownloadingSource(this);
533 if (byNewState == DS_DOWNLOADING) {
534 msReceivedPrev = GetTickCount();
535 theStats::AddDownloadingSource();
536 } else if (m_nDownloadState == DS_DOWNLOADING) {
537 theStats::RemoveDownloadingSource();
540 if (m_nDownloadState == DS_DOWNLOADING) {
541 m_nDownloadState = byNewState;
542 ClearDownloadBlockRequests();
544 kBpsDown = 0.0;
545 bytesReceivedCycle = 0;
546 msReceivedPrev = 0;
547 if (byNewState == DS_NONE) {
548 if (m_reqfile) {
549 m_reqfile->UpdatePartsFrequency( this, false ); // Decrement
551 m_downPartStatus.clear();
552 m_nPartCount = 0;
554 if (m_socket && byNewState != DS_ERROR) {
555 m_socket->DisableDownloadLimit();
558 m_nDownloadState = byNewState;
559 if(GetDownloadState() == DS_DOWNLOADING) {
560 if (IsEmuleClient()) {
561 SetRemoteQueueFull(false);
563 SetRemoteQueueRank(0); // eMule 0.30c set like this ...
565 UpdateDisplayedInfo(true);
568 /* eMule 0.30c implementation, i give it a try (Creteil) END ... */
570 void CUpDownClient::ProcessHashSet(const byte* packet, uint32 size)
572 if ((!m_reqfile) || md4cmp(packet,m_reqfile->GetFileHash().GetHash())) {
573 throw wxString(wxT("Wrong fileid sent (ProcessHashSet)"));
575 if (!m_fHashsetRequesting) {
576 throw wxString(wxT("Received unsolicited hashset, ignoring it."));
578 CMemFile data(packet,size);
579 if (m_reqfile->LoadHashsetFromFile(&data,true)) {
580 m_fHashsetRequesting = 0;
581 } else {
582 m_reqfile->SetHashSetNeeded(true);
583 throw wxString(wxT("Corrupted or invalid hashset received"));
585 SendStartupLoadReq();
588 void CUpDownClient::SendBlockRequests()
590 uint32 current_time = ::GetTickCount();
591 if (GetVBTTags()) {
593 // Ask new blocks only when all completed
594 if (m_PendingBlocks_list.size()) {
595 return;
598 if ((m_dwLastBlockReceived + SEC2MS(5)) > current_time) {
599 // We received last block in less than 5 secs? Let's request faster.
600 m_MaxBlockRequests = m_MaxBlockRequests << 1;
601 if ( m_MaxBlockRequests > 0x20) {
602 m_MaxBlockRequests = 0x20;
604 } else {
605 m_MaxBlockRequests = m_MaxBlockRequests >> 1;
606 if ( m_MaxBlockRequests < STANDARD_BLOCKS_REQUEST) {
607 m_MaxBlockRequests = STANDARD_BLOCKS_REQUEST;
612 m_dwLastBlockReceived = current_time;
614 if (!m_reqfile) {
615 return;
618 uint8 version = GetVBTTags() ? 2 : 1;
620 if (m_DownloadBlocks_list.empty()) {
621 // Barry - instead of getting 3, just get how many is needed
622 uint16 count = m_MaxBlockRequests - m_PendingBlocks_list.size();
623 std::vector<Requested_Block_Struct*> toadd;
624 if (m_reqfile->GetNextRequestedBlock(this, toadd, count)) {
625 for (int i = 0; i != count; i++) {
626 m_DownloadBlocks_list.push_back(toadd[i]);
631 // Barry - Why are unfinished blocks requested again, not just new ones?
633 while (m_PendingBlocks_list.size() < m_MaxBlockRequests && !m_DownloadBlocks_list.empty()) {
634 Pending_Block_Struct* pblock = new Pending_Block_Struct;
635 pblock->block = m_DownloadBlocks_list.front();
636 pblock->zStream = NULL;
637 pblock->totalUnzipped = 0;
638 pblock->fZStreamError = 0;
639 pblock->fRecovered = 0;
640 m_PendingBlocks_list.push_back(pblock);
641 m_DownloadBlocks_list.pop_front();
645 if (m_PendingBlocks_list.empty()) {
647 CUpDownClient* slower_client = NULL;
649 if (thePrefs::GetDropSlowSources()) {
650 slower_client = m_reqfile->GetSlowerDownloadingClient(m_lastaverage, this);
653 if (slower_client == NULL) {
654 slower_client = this;
657 if (!slower_client->GetSentCancelTransfer()) {
658 CPacket* packet = new CPacket(OP_CANCELTRANSFER, 0, OP_EDONKEYPROT);
659 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
660 if (slower_client != this) {
661 // printf("Dropped client %p to allow client %p to download\n",slower_client, this);
663 slower_client->ClearDownloadBlockRequests();
664 slower_client->SendPacket(packet,true,true);
665 slower_client->SetSentCancelTransfer(1);
668 slower_client->SetDownloadState(DS_NONEEDEDPARTS);
670 if (slower_client != this) {
671 // Re-request freed blocks.
672 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_CANCELTRANSFER (faster source eager to transfer) to ") + slower_client->GetFullIP() );
673 wxASSERT(m_DownloadBlocks_list.empty());
674 wxASSERT(m_PendingBlocks_list.empty());
675 uint16 count = m_MaxBlockRequests;
676 std::vector<Requested_Block_Struct*> toadd;
677 if (m_reqfile->GetNextRequestedBlock(this, toadd, count)) {
678 for (int i = 0; i != count; i++) {
679 Pending_Block_Struct* pblock = new Pending_Block_Struct;
680 pblock->block = toadd[i];
681 pblock->zStream = NULL;
682 pblock->totalUnzipped = 0;
683 pblock->fZStreamError = 0;
684 pblock->fRecovered = 0;
685 m_PendingBlocks_list.push_back(pblock);
687 } else {
688 // WTF, we just freed blocks.
689 wxFAIL;
690 return;
692 } else {
693 // Drop this one.
694 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_CANCELTRANSFER (no free blocks) to ") + GetFullIP() );
695 //#warning Kry - Would be nice to swap A4AF here.
696 return;
700 CPacket* packet = NULL;
702 switch (version) {
703 case 2: {
704 // ED2Kv2 packet...
705 // Most common scenario: hash + blocks to request + every one
706 // having 2 uint32 tags
708 uint8 nBlocks = m_PendingBlocks_list.size();
709 if (nBlocks > m_MaxBlockRequests) {
710 nBlocks = m_MaxBlockRequests;
713 CMemFile data(16 + 1 + nBlocks*((2+4)*2));
715 data.WriteHash(m_reqfile->GetFileHash());
717 data.WriteUInt8(nBlocks);
719 std::list<Pending_Block_Struct*>::iterator it = m_PendingBlocks_list.begin();
720 while (nBlocks) {
721 wxASSERT(it != m_PendingBlocks_list.end());
722 wxASSERT( (*it)->block->StartOffset <= (*it)->block->EndOffset );
723 (*it)->fZStreamError = 0;
724 (*it)->fRecovered = 0;
725 CTagVarInt(/*Noname*/0,(*it)->block->StartOffset).WriteTagToFile(&data);
726 CTagVarInt(/*Noname*/0,(*it)->block->EndOffset).WriteTagToFile(&data);
727 ++it;
728 nBlocks--;
731 packet = new CPacket(data, OP_ED2KV2HEADER, OP_REQUESTPARTS);
732 AddDebugLogLineM( false, logLocalClient, CFormat(wxT("Local Client ED2Kv2: OP_REQUESTPARTS(%i) to %s"))
733 % (m_PendingBlocks_list.size()<m_MaxBlockRequests ? m_PendingBlocks_list.size() : m_MaxBlockRequests) % GetFullIP() );
735 break;
737 case 1: {
738 wxASSERT(m_MaxBlockRequests == STANDARD_BLOCKS_REQUEST);
740 //#warning Kry - I dont specially like this approach, we iterate one time too many
742 bool bHasLongBlocks = false;
744 std::list<Pending_Block_Struct*>::iterator it = m_PendingBlocks_list.begin();
745 for (uint32 i = 0; i != m_MaxBlockRequests; i++){
746 if (it != m_PendingBlocks_list.end()) {
747 Pending_Block_Struct* pending = *it++;
748 wxASSERT( pending->block->StartOffset <= pending->block->EndOffset );
749 if (pending->block->StartOffset > 0xFFFFFFFF || pending->block->EndOffset > 0xFFFFFFFF){
750 bHasLongBlocks = true;
751 if (!SupportsLargeFiles()){
752 // Requesting a large block from a client that doesn't support large files?
753 wxFAIL;
754 if (!GetSentCancelTransfer()){
755 CPacket* cancel_packet = new CPacket(OP_CANCELTRANSFER, 0, OP_EDONKEYPROT);
756 theStats::AddUpOverheadFileRequest(cancel_packet->GetPacketSize());
757 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
758 SendPacket(cancel_packet,true,true);
759 SetSentCancelTransfer(1);
761 SetDownloadState(DS_ERROR);
763 break;
768 CMemFile data(16 /*Hash*/ + (m_MaxBlockRequests*(bHasLongBlocks ? 8 : 4) /* uint32/64 start*/) + (3*(bHasLongBlocks ? 8 : 4)/* uint32/64 end*/));
769 data.WriteHash(m_reqfile->GetFileHash());
771 it = m_PendingBlocks_list.begin();
772 for (uint32 i = 0; i != m_MaxBlockRequests; i++) {
773 if (it != m_PendingBlocks_list.end()) {
774 Pending_Block_Struct* pending = *it++;
775 wxASSERT( pending->block->StartOffset <= pending->block->EndOffset );
776 pending->fZStreamError = 0;
777 pending->fRecovered = 0;
778 if (bHasLongBlocks) {
779 data.WriteUInt64(pending->block->StartOffset);
780 } else {
781 data.WriteUInt32(pending->block->StartOffset);
783 } else {
784 if (bHasLongBlocks) {
785 data.WriteUInt64(0);
786 } else {
787 data.WriteUInt32(0);
792 it = m_PendingBlocks_list.begin();
793 for (uint32 i = 0; i != m_MaxBlockRequests; i++) {
794 if (it != m_PendingBlocks_list.end()) {
795 Requested_Block_Struct* block = (*it++)->block;
796 if (bHasLongBlocks) {
797 data.WriteUInt64(block->EndOffset+1);
798 } else {
799 data.WriteUInt32(block->EndOffset+1);
801 } else {
802 if (bHasLongBlocks) {
803 data.WriteUInt64(0);
804 } else {
805 data.WriteUInt32(0);
809 packet = new CPacket(data, (bHasLongBlocks ? OP_EMULEPROT : OP_EDONKEYPROT), (bHasLongBlocks ? (uint8)OP_REQUESTPARTS_I64 : (uint8)OP_REQUESTPARTS));
810 AddDebugLogLineM( false, logLocalClient, wxString::Format(wxT("Local Client: %s to "),(bHasLongBlocks ? wxT("OP_REQUESTPARTS_I64") : wxT("OP_REQUESTPARTS"))) + GetFullIP() );
811 break;
813 default:
814 wxFAIL;
817 if (packet) {
818 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
819 SendPacket(packet, true, true);
820 } else {
821 wxFAIL;
826 Barry - Originally this only wrote to disk when a full 180k block
827 had been received from a client, and only asked for data in
828 180k blocks.
830 This meant that on average 90k was lost for every connection
831 to a client data source. That is a lot of wasted data.
833 To reduce the lost data, packets are now written to a buffer
834 and flushed to disk regularly regardless of size downloaded.
836 This includes compressed packets.
838 Data is also requested only where gaps are, not in 180k blocks.
839 The requests will still not exceed 180k, but may be smaller to
840 fill a gap.
843 void CUpDownClient::ProcessBlockPacket(const byte* packet, uint32 size, bool packed, bool largeblocks)
845 // Ignore if no data required
846 if (!(GetDownloadState() == DS_DOWNLOADING || GetDownloadState() == DS_NONEEDEDPARTS)) {
847 return;
850 // This vars are defined here to be able to use them on the catch
851 int header_size = 16;
852 uint64 nStartPos = 0;
853 uint64 nEndPos = 0;
854 uint32 nBlockSize = 0;
855 uint32 lenUnzipped = 0;
857 // Update stats
858 m_dwLastBlockReceived = ::GetTickCount();
860 try {
862 // Read data from packet
863 const CMemFile data(packet, size);
865 // Check that this data is for the correct file
866 if ((!m_reqfile) || data.ReadHash() != m_reqfile->GetFileHash()) {
867 throw wxString(wxT("Wrong fileid sent (ProcessBlockPacket)"));
870 // Find the start & end positions, and size of this chunk of data
872 if (largeblocks) {
873 nStartPos = data.ReadUInt64();
874 header_size += 8;
875 } else {
876 nStartPos = data.ReadUInt32();
877 header_size += 4;
880 if (packed) {
881 nBlockSize = data.ReadUInt32();
882 header_size += 4;
883 nEndPos = nStartPos + (size - header_size);
884 } else {
885 if (largeblocks) {
886 nEndPos = data.ReadUInt64();
887 header_size += 8;
888 } else {
889 nEndPos = data.ReadUInt32();
890 header_size += 4;
894 // Check that packet size matches the declared data size + header size
895 if ( nEndPos == nStartPos || size != ((nEndPos - nStartPos) + header_size)) {
896 throw wxString(wxT("Corrupted or invalid DataBlock received (ProcessBlockPacket)"));
898 theStats::AddDownloadFromSoft(GetClientSoft(),size - header_size);
899 bytesReceivedCycle += size - header_size;
901 credits->AddDownloaded(size - header_size, GetIP(), theApp->CryptoAvailable());
903 // Move end back one, should be inclusive
904 nEndPos--;
906 // Loop through to find the reserved block that this is within
907 std::list<Pending_Block_Struct*>::iterator it = m_PendingBlocks_list.begin();
908 for (; it != m_PendingBlocks_list.end(); ++it) {
909 Pending_Block_Struct* cur_block = *it;
911 if ((cur_block->block->StartOffset <= nStartPos) && (cur_block->block->EndOffset >= nStartPos)) {
912 // Found reserved block
914 if (cur_block->block->StartOffset == nStartPos) {
915 // This block just started transfering. Set the start time.
916 m_last_block_start = ::GetTickCountFullRes();
919 if (cur_block->fZStreamError){
920 AddDebugLogLineM( false, logZLib,
921 CFormat(wxT("Ignoring %u bytes of block %u-%u because of erroneous zstream state for file: %s"))
922 % (size - header_size) % nStartPos % nEndPos % m_reqfile->GetFileName());
923 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
924 return;
927 // Remember this start pos, used to draw part downloading in list
928 m_nLastBlockOffset = nStartPos;
930 // Occasionally packets are duplicated, no point writing it twice
931 // This will be 0 in these cases, or the length written otherwise
932 uint32 lenWritten = 0;
934 // Handle differently depending on whether packed or not
935 if (!packed) {
936 // security sanitize check
937 if (nEndPos > cur_block->block->EndOffset) {
938 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()));
939 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
940 return;
942 // Write to disk (will be buffered in part file class)
943 lenWritten = m_reqfile->WriteToBuffer( size - header_size, (byte*)(packet + header_size), nStartPos, nEndPos, cur_block->block, this);
944 } else {
945 // Packed
946 wxASSERT( (long int)size > 0 );
947 // Create space to store unzipped data, the size is
948 // only an initial guess, will be resized in unzip()
949 // if not big enough
950 lenUnzipped = (size * 2);
951 // Don't get too big
952 if (lenUnzipped > (BLOCKSIZE + 300)) {
953 lenUnzipped = (BLOCKSIZE + 300);
955 byte *unzipped = new byte[lenUnzipped];
957 // Try to unzip the packet
958 int result = unzip(cur_block, (byte*)(packet + header_size), (size - header_size), &unzipped, &lenUnzipped);
960 // no block can be uncompressed to >2GB, 'lenUnzipped' is obviously erroneous.
961 if (result == Z_OK && ((int)lenUnzipped >= 0)) {
963 // Write any unzipped data to disk
964 if (lenUnzipped > 0) {
965 wxASSERT( (int)lenUnzipped > 0 );
967 // Use the current start and end positions for the uncompressed data
968 nStartPos = cur_block->block->StartOffset + cur_block->totalUnzipped - lenUnzipped;
969 nEndPos = cur_block->block->StartOffset + cur_block->totalUnzipped - 1;
971 if (nStartPos > cur_block->block->EndOffset || nEndPos > cur_block->block->EndOffset) {
972 AddDebugLogLineM( false, logZLib,
973 CFormat(wxT("Corrupted compressed packet for '%s' received (error 666)")) % m_reqfile->GetFileName());
974 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
975 } else {
976 // Write uncompressed data to file
977 lenWritten = m_reqfile->WriteToBuffer( size - header_size,
978 unzipped,
979 nStartPos,
980 nEndPos,
981 cur_block->block,
982 this);
985 } else {
986 wxString strZipError;
987 if (cur_block->zStream && cur_block->zStream->msg) {
988 strZipError = wxT(" - ") + wxString::FromAscii(cur_block->zStream->msg);
991 AddDebugLogLineM( false, logZLib,
992 CFormat(wxT("Corrupted compressed packet for '%s' received (error %i): %s"))
993 % m_reqfile->GetFileName() % result % strZipError);
995 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
997 // If we had an zstream error, there is no chance that we could recover from it nor that we
998 // could use the current zstream (which is in error state) any longer.
999 if (cur_block->zStream){
1000 inflateEnd(cur_block->zStream);
1001 delete cur_block->zStream;
1002 cur_block->zStream = NULL;
1005 // Although we can't further use the current zstream, there is no need to disconnect the sending
1006 // client because the next zstream (a series of 10K-blocks which build a 180K-block) could be
1007 // valid again. Just ignore all further blocks for the current zstream.
1008 cur_block->fZStreamError = 1;
1009 cur_block->totalUnzipped = 0; // bluecow's fix
1011 delete [] unzipped;
1013 // These checks only need to be done if any data was written
1014 if (lenWritten > 0) {
1015 m_nTransferredDown += lenWritten;
1017 // If finished reserved block
1018 if (nEndPos == cur_block->block->EndOffset) {
1020 // Save last average speed based on data and time.
1021 // This should do bytes/sec.
1022 uint32 average_time = (::GetTickCountFullRes() - m_last_block_start);
1024 // Avoid divide by 0.
1025 if (average_time == 0) {
1026 average_time++;
1029 m_lastaverage = ((cur_block->block->EndOffset - cur_block->block->StartOffset) * 1000) / average_time;
1031 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
1032 delete cur_block->block;
1033 // Not always allocated
1034 if (cur_block->zStream) {
1035 inflateEnd(cur_block->zStream);
1036 delete cur_block->zStream;
1038 delete cur_block;
1039 m_PendingBlocks_list.erase(it);
1041 // Request next block
1042 SendBlockRequests();
1045 // Stop looping and exit method
1046 return;
1049 } catch (const CEOFException& e) {
1050 wxString error = wxString(wxT("Error reading "));
1051 if (packed) error += CFormat(wxT("packed (LU: %i) largeblocks ")) % lenUnzipped;
1052 error += CFormat(wxT("data packet: RS: %i HS: %i SP: %i EP: %i BS: %i -> "))
1053 % size % header_size % nStartPos % nEndPos % nBlockSize;
1054 AddDebugLogLineM(true, logRemoteClient, error + e.what());
1055 return;
1059 int CUpDownClient::unzip(Pending_Block_Struct *block, byte *zipped, uint32 lenZipped, byte **unzipped, uint32 *lenUnzipped, int iRecursion)
1061 int err = Z_DATA_ERROR;
1063 // Save some typing
1064 z_stream *zS = block->zStream;
1066 // Is this the first time this block has been unzipped
1067 if (zS == NULL) {
1068 // Create stream
1069 block->zStream = new z_stream;
1070 zS = block->zStream;
1072 // Initialise stream values
1073 zS->zalloc = (alloc_func)0;
1074 zS->zfree = (free_func)0;
1075 zS->opaque = (voidpf)0;
1077 // Set output data streams, do this here to avoid overwriting on recursive calls
1078 zS->next_out = (*unzipped);
1079 zS->avail_out = (*lenUnzipped);
1081 // Initialise the z_stream
1082 err = inflateInit(zS);
1083 if (err != Z_OK) {
1084 return err;
1088 // Use whatever input is provided
1089 zS->next_in = zipped;
1090 zS->avail_in = lenZipped;
1092 // Only set the output if not being called recursively
1093 if (iRecursion == 0) {
1094 zS->next_out = (*unzipped);
1095 zS->avail_out = (*lenUnzipped);
1098 // Try to unzip the data
1099 err = inflate(zS, Z_SYNC_FLUSH);
1101 // Is zip finished reading all currently available input and writing
1102 // all generated output
1103 if (err == Z_STREAM_END) {
1104 // Finish up
1105 err = inflateEnd(zS);
1106 if (err != Z_OK) {
1107 return err;
1110 // Got a good result, set the size to the amount unzipped in this call
1111 // (including all recursive calls)
1112 (*lenUnzipped) = (zS->total_out - block->totalUnzipped);
1113 block->totalUnzipped = zS->total_out;
1114 } else if ((err == Z_OK) && (zS->avail_out == 0) && (zS->avail_in != 0)) {
1116 // Output array was not big enough,
1117 // call recursively until there is enough space
1119 // What size should we try next
1120 uint32 newLength = (*lenUnzipped) *= 2;
1121 if (newLength == 0) {
1122 newLength = lenZipped * 2;
1124 // Copy any data that was successfully unzipped to new array
1125 byte *temp = new byte[newLength];
1126 wxASSERT( zS->total_out - block->totalUnzipped <= newLength );
1127 memcpy(temp, (*unzipped), (zS->total_out - block->totalUnzipped));
1128 delete [] (*unzipped);
1129 (*unzipped) = temp;
1130 (*lenUnzipped) = newLength;
1132 // Position stream output to correct place in new array
1133 zS->next_out = (*unzipped) + (zS->total_out - block->totalUnzipped);
1134 zS->avail_out = (*lenUnzipped) - (zS->total_out - block->totalUnzipped);
1136 // Try again
1137 err = unzip(block, zS->next_in, zS->avail_in, unzipped, lenUnzipped, iRecursion + 1);
1138 } else if ((err == Z_OK) && (zS->avail_in == 0)) {
1139 // All available input has been processed, everything ok.
1140 // Set the size to the amount unzipped in this call
1141 // (including all recursive calls)
1142 (*lenUnzipped) = (zS->total_out - block->totalUnzipped);
1143 block->totalUnzipped = zS->total_out;
1144 } else {
1145 // Should not get here unless input data is corrupt
1146 wxString strZipError;
1148 if ( zS->msg ) {
1149 strZipError = wxString::Format(wxT(" %d '"), err) + wxString::FromAscii(zS->msg) + wxT("'");
1150 } else if (err != Z_OK) {
1151 strZipError = wxString::Format(wxT(" %d"), err);
1154 AddDebugLogLineM(false, logZLib,
1155 CFormat(wxT("Unexpected zip error %s in file '%s'"))
1156 % strZipError % (m_reqfile ? m_reqfile->GetFileName() : CPath(wxT("?"))));
1159 if (err != Z_OK) {
1160 (*lenUnzipped) = 0;
1163 return err;
1167 // Speed is now updated only when data was received, calculated as
1168 // (data received) / (time since last receiption)
1169 // and slightly filtered (10s average).
1170 // Result is quite precise now and makes the DownloadRateAdjust workaround obsolete.
1172 float CUpDownClient::CalculateKBpsDown()
1174 const float tAverage = 10.0;
1175 uint32 msCur = GetTickCount();
1177 if (bytesReceivedCycle) {
1178 float dt = (msCur - msReceivedPrev) / 1000.0; // time since last reception
1179 if (dt < 0.01) { // (safeguard against divide-by-zero)
1180 dt = 0.01f; // diff should be 100ms actually
1182 float kBpsDownCur = bytesReceivedCycle / 1024.0 / dt;
1183 if (dt >= tAverage) {
1184 kBpsDown = kBpsDownCur;
1185 } else {
1186 kBpsDown = (kBpsDown * (tAverage - dt) + kBpsDownCur * dt) / tAverage;
1188 //AddDebugLogLineM( false, logLocalClient, CFormat(wxT("CalculateKBpsDown %p kbps %.1f kbpsCur %.1f dt %.3f rcv %d "))
1189 // % this % kBpsDown % kBpsDownCur % dt % bytesReceivedCycle);
1190 bytesReceivedCycle = 0;
1191 msReceivedPrev = msCur;
1194 m_cShowDR++;
1195 if (m_cShowDR == 30){
1196 m_cShowDR = 0;
1197 UpdateDisplayedInfo();
1199 if (msCur - m_dwLastBlockReceived > DOWNLOADTIMEOUT) {
1200 if (!GetSentCancelTransfer()){
1201 CPacket* packet = new CPacket(OP_CANCELTRANSFER, 0, OP_EDONKEYPROT);
1202 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
1203 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
1204 SendPacket(packet,true,true);
1205 SetSentCancelTransfer(1);
1207 SetDownloadState(DS_ONQUEUE);
1210 return kBpsDown;
1213 uint16 CUpDownClient::GetAvailablePartCount() const
1215 uint16 result = 0;
1216 for (int i = 0;i != m_nPartCount;i++){
1217 if (IsPartAvailable(i))
1218 result++;
1220 return result;
1223 void CUpDownClient::SetRemoteQueueRank(uint16 nr)
1225 m_nOldRemoteQueueRank = m_nRemoteQueueRank;
1226 m_nRemoteQueueRank = nr;
1227 UpdateDisplayedInfo();
1230 void CUpDownClient::UDPReaskACK(uint16 nNewQR)
1232 // 0.42e
1233 m_bUDPPending = false;
1234 SetRemoteQueueRank(nNewQR);
1235 m_dwLastAskedTime = ::GetTickCount();
1238 void CUpDownClient::UDPReaskFNF()
1240 m_bUDPPending = false;
1242 // avoid premature deletion of 'this' client
1243 if (GetDownloadState() != DS_DOWNLOADING){
1244 if (m_reqfile) {
1245 m_reqfile->AddDeadSource(this);
1248 theApp->downloadqueue->RemoveSource(this);
1249 if (!m_socket) {
1250 if (Disconnected(wxT("UDPReaskFNF m_socket=NULL"))) {
1251 Safe_Delete();
1254 } else {
1255 AddDebugLogLineM( false, logRemoteClient, wxT("UDP ANSWER FNF : ") + GetUserName() + wxT(" - did not remove client because of current download state") );
1259 void CUpDownClient::UDPReaskForDownload()
1262 wxASSERT(m_reqfile);
1264 if(!m_reqfile || m_bUDPPending ) {
1265 return;
1268 //#warning We should implement the quality tests for udp reliability
1270 if( m_nTotalUDPPackets > 3 && ((float)(m_nFailedUDPPackets/m_nTotalUDPPackets) > .3)) {
1271 return;
1275 if (thePrefs::GetEffectiveUDPPort() == 0) {
1276 return;
1279 if (m_nUDPPort != 0 && !theApp->IsFirewalled() && !IsConnected()) {
1280 //don't use udp to ask for sources
1281 if(IsSourceRequestAllowed()) {
1282 return;
1285 m_bUDPPending = true;
1287 CMemFile data(128);
1288 data.WriteHash(m_reqfile->GetFileHash());
1290 if (GetUDPVersion() > 3) {
1291 if (m_reqfile->IsPartFile()) {
1292 ((CPartFile*)m_reqfile)->WritePartStatus(&data);
1294 else {
1295 data.WriteUInt16(0);
1299 if (GetUDPVersion() > 2) {
1300 data.WriteUInt16(m_reqfile->m_nCompleteSourcesCount);
1303 CPacket* response = new CPacket(data, OP_EMULEPROT, OP_REASKFILEPING);
1304 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket: send OP_REASKFILEPING") );
1305 theStats::AddUpOverheadFileRequest(response->GetPacketSize());
1306 theApp->clientudp->SendPacket(response,GetConnectIP(),GetUDPPort(), ShouldReceiveCryptUDPPackets(), GetUserHash().GetHash(), false, 0);
1307 } else if (HasLowID() && GetBuddyIP() && GetBuddyPort() && HasValidBuddyID()) {
1309 m_bUDPPending = true;
1311 CMemFile data(128);
1313 data.WriteHash(CMD4Hash(GetBuddyID()));
1314 data.WriteHash(m_reqfile->GetFileHash());
1316 if (GetUDPVersion() > 3) {
1317 if (m_reqfile->IsPartFile()) {
1318 ((CPartFile*)m_reqfile)->WritePartStatus(&data);
1319 } else {
1320 data.WriteUInt16(0);
1324 if (GetUDPVersion() > 2) {
1325 data.WriteUInt16(m_reqfile->m_nCompleteSourcesCount);
1328 CPacket* response = new CPacket(data, OP_EMULEPROT, OP_REASKCALLBACKUDP);
1329 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket: send OP_REASKCALLBACKUDP") );
1330 theStats::AddUpOverheadFileRequest(response->GetPacketSize());
1331 theApp->clientudp->SendPacket(response, GetBuddyIP(), GetBuddyPort(), false, NULL, true, 0 );
1336 //! Barry - Sets string to show parts downloading, eg NNNYNNNNYYNYN
1337 wxString CUpDownClient::ShowDownloadingParts() const
1339 // Initialise to all N's
1340 wxString Parts(wxT('N'), m_nPartCount);
1342 std::list<Pending_Block_Struct*>::const_iterator it = m_PendingBlocks_list.begin();
1343 for (; it != m_PendingBlocks_list.end(); ++it) {
1344 Parts.SetChar(((*it)->block->StartOffset / PARTSIZE), 'Y');
1347 return Parts;
1351 void CUpDownClient::UpdateDisplayedInfo(bool force)
1353 uint32 curTick = ::GetTickCount();
1354 if(force || curTick-m_lastRefreshedDLDisplay > MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE) {
1355 // Check if we actually need to notify of changes
1356 bool update = m_reqfile && m_reqfile->ShowSources();
1358 // Check A4AF files only if needed
1359 if ( !update ) {
1360 A4AFList::iterator it = m_A4AF_list.begin();
1361 for ( ; it != m_A4AF_list.end(); ++it ) {
1362 if ( it->first->ShowSources() ) {
1363 update = true;
1364 break;
1369 // And finnaly trigger an event if there's any reason
1370 if ( update ) {
1371 Notify_DownloadCtrlUpdateItem(this);
1374 m_lastRefreshedDLDisplay = curTick;
1379 // IgnoreNoNeeded = will switch to files of which this source has no needed parts (if no better fiels found)
1380 // ignoreSuspensions = ignore timelimit for A4Af jumping
1381 // bRemoveCompletely = do not readd the file which the source is swapped from to the A4AF lists (needed if deleting or stopping a file)
1382 // toFile = Try to swap to this partfile only
1384 bool CUpDownClient::SwapToAnotherFile(bool bIgnoreNoNeeded, bool ignoreSuspensions, bool bRemoveCompletely, CPartFile* toFile)
1386 // Fail if m_reqfile is invalid
1387 if ( m_reqfile == NULL ) {
1388 return false;
1391 // It would be stupid to swap away a downloading source
1392 if (GetDownloadState() == DS_DOWNLOADING) {
1393 return false;
1396 // The iterator of the final target
1397 A4AFList::iterator target = m_A4AF_list.end();
1399 // Do we want to swap to a specific file?
1400 if ( toFile != NULL ) {
1401 A4AFList::iterator it = m_A4AF_list.find( toFile );
1402 if ( it != m_A4AF_list.end() ) {
1404 // We force ignoring of noneeded flag and timestamps
1405 if ( IsValidSwapTarget( it, true, true ) ) {
1406 // Set the target
1407 target = it;
1410 } else {
1411 // We want highest priority possible, but need to start with
1412 // a value less than any other priority
1413 char priority = -1;
1415 A4AFList::iterator it = m_A4AF_list.begin();
1416 for ( ; it != m_A4AF_list.end(); ++it ) {
1417 if ( IsValidSwapTarget( it, bIgnoreNoNeeded, ignoreSuspensions ) ) {
1418 char cur_priority = it->first->GetDownPriority();
1420 // We would prefer to get files with needed parts, thus rate them higher.
1421 // However, this really only matters if bIgnoreNoNeeded is true.
1422 if ( it->second.NeededParts )
1423 cur_priority += 10;
1425 // Change target if the current file has a higher rate than the previous
1426 if ( cur_priority > priority ) {
1427 priority = cur_priority;
1429 // Set the new target
1430 target = it;
1432 // Break on the first High-priority file with needed parts
1433 if ( priority == PR_HIGH + 10 ) {
1434 break;
1441 // Try to swap if we found a valid target
1442 if ( target != m_A4AF_list.end() ) {
1444 // Sainity check, if reqfile doesn't own the source, then something
1445 // is wrong and the swap cannot proceed.
1446 if ( m_reqfile->DelSource( this ) ) {
1447 CPartFile* SwapTo = target->first;
1449 // remove this client from the A4AF list of our new m_reqfile
1450 if ( SwapTo->RemoveA4AFSource( this ) ) {
1451 Notify_DownloadCtrlRemoveSource(this, SwapTo);
1454 m_reqfile->RemoveDownloadingSource( this );
1456 // Do we want to remove it completly? Say if the old file is getting deleted
1457 if ( !bRemoveCompletely ) {
1458 m_reqfile->AddA4AFSource( this );
1460 // Set the status of the old file
1461 m_A4AF_list[m_reqfile].NeededParts = (GetDownloadState() != DS_NONEEDEDPARTS);
1463 // Avoid swapping to this file for a while
1464 m_A4AF_list[m_reqfile].timestamp = ::GetTickCount();
1466 Notify_DownloadCtrlAddSource(m_reqfile, this, A4AF_SOURCE);
1467 } else {
1468 Notify_DownloadCtrlRemoveSource( this, m_reqfile );
1471 SetDownloadState(DS_NONE);
1472 ResetFileStatusInfo();
1474 m_nRemoteQueueRank = 0;
1475 m_nOldRemoteQueueRank = 0;
1477 m_reqfile->UpdatePartsInfo();
1479 SetRequestFile( SwapTo );
1481 SwapTo->AddSource( this );
1483 Notify_DownloadCtrlAddSource(SwapTo, this, UNAVAILABLE_SOURCE);
1485 // Remove the new reqfile from the list of other files
1486 m_A4AF_list.erase( target );
1488 return true;
1492 return false;
1496 bool CUpDownClient::IsValidSwapTarget( A4AFList::iterator it, bool ignorenoneeded, bool ignoresuspended )
1498 wxASSERT( it != m_A4AF_list.end() && it->first );
1500 // Check if this file has been suspended
1501 if ( !ignoresuspended ) {
1502 if ( ::GetTickCount() - it->second.timestamp >= PURGESOURCESWAPSTOP ) {
1503 // The wait-time has been exceeded and the file is now a valid target
1504 it->second.timestamp = 0;
1505 } else {
1506 // The file was still suspended and we are not ignoring suspensions
1507 return false;
1511 // Check if the client has needed parts
1512 if ( !ignorenoneeded ) {
1513 if ( !it->second.NeededParts ) {
1514 return false;
1518 // Final checks to see if the client is a valid target
1519 CPartFile* cur_file = it->first;
1520 if ( ( cur_file != m_reqfile && !cur_file->IsStopped() ) &&
1521 ( cur_file->GetStatus() == PS_READY || cur_file->GetStatus() == PS_EMPTY ) &&
1522 ( cur_file->IsPartFile() ) )
1524 return true;
1525 } else {
1526 return false;
1531 void CUpDownClient::SetRequestFile(CPartFile* reqfile)
1533 if ( m_reqfile != reqfile ) {
1534 // Decrement the source-count of the old request-file
1535 if ( m_reqfile ) {
1536 m_reqfile->ClientStateChanged( GetDownloadState(), -1 );
1537 m_reqfile->UpdatePartsFrequency( this, false );
1540 m_nPartCount = 0;
1541 m_downPartStatus.clear();
1543 m_reqfile = reqfile;
1545 if ( reqfile ) {
1546 // Increment the source-count of the new request-file
1547 m_reqfile->ClientStateChanged( -1, GetDownloadState() );
1549 m_nPartCount = reqfile->GetPartCount();
1554 void CUpDownClient::SetReqFileAICHHash(CAICHHash* val){
1555 if(m_pReqFileAICHHash != NULL && m_pReqFileAICHHash != val)
1556 delete m_pReqFileAICHHash;
1557 m_pReqFileAICHHash = val;
1560 void CUpDownClient::SendAICHRequest(CPartFile* pForFile, uint16 nPart){
1561 CAICHRequestedData request;
1562 request.m_nPart = nPart;
1563 request.m_pClient = this;
1564 request.m_pPartFile = pForFile;
1565 CAICHHashSet::m_liRequestedData.push_back(request);
1566 m_fAICHRequested = TRUE;
1567 CMemFile data;
1568 data.WriteHash(pForFile->GetFileHash());
1569 data.WriteUInt16(nPart);
1570 pForFile->GetAICHHashset()->GetMasterHash().Write(&data);
1571 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_AICHREQUEST);
1572 theStats::AddUpOverheadOther(packet->GetPacketSize());
1573 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_AICHREQUEST to") + GetFullIP());
1574 SafeSendPacket(packet);
1577 void CUpDownClient::ProcessAICHAnswer(const byte* packet, uint32 size)
1579 if (m_fAICHRequested == FALSE){
1580 throw wxString(wxT("Received unrequested AICH Packet"));
1582 m_fAICHRequested = FALSE;
1584 CMemFile data(packet, size);
1585 if (size <= 16){
1586 CAICHHashSet::ClientAICHRequestFailed(this);
1587 return;
1590 CMD4Hash hash = data.ReadHash();
1591 CPartFile* pPartFile = theApp->downloadqueue->GetFileByID(hash);
1592 CAICHRequestedData request = CAICHHashSet::GetAICHReqDetails(this);
1593 uint16 nPart = data.ReadUInt16();
1594 if (pPartFile != NULL && request.m_pPartFile == pPartFile && request.m_pClient == this && nPart == request.m_nPart){
1595 CAICHHash ahMasterHash(&data);
1596 if ( (pPartFile->GetAICHHashset()->GetStatus() == AICH_TRUSTED || pPartFile->GetAICHHashset()->GetStatus() == AICH_VERIFIED)
1597 && ahMasterHash == pPartFile->GetAICHHashset()->GetMasterHash())
1599 if(pPartFile->GetAICHHashset()->ReadRecoveryData(request.m_nPart*PARTSIZE, &data)){
1600 // finally all checks passed, everythings seem to be fine
1601 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1602 CAICHHashSet::RemoveClientAICHRequest(this);
1603 pPartFile->AICHRecoveryDataAvailable(request.m_nPart);
1604 return;
1605 } else {
1606 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1608 } else {
1609 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Answer: Masterhash differs from packethash or hashset has no trusted Masterhash") );
1611 } else {
1612 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Answer: requested values differ from values in packet") );
1615 CAICHHashSet::ClientAICHRequestFailed(this);
1619 void CUpDownClient::ProcessAICHRequest(const byte* packet, uint32 size)
1621 if (size != 16 + 2 + CAICHHash::GetHashSize()) {
1622 throw wxString(wxT("Received AICH Request Packet with wrong size"));
1625 CMemFile data(packet, size);
1627 CMD4Hash hash = data.ReadHash();
1628 uint16 nPart = data.ReadUInt16();
1629 CAICHHash ahMasterHash(&data);
1630 CKnownFile* pKnownFile = theApp->sharedfiles->GetFileByID(hash);
1631 if (pKnownFile != NULL){
1632 if (pKnownFile->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE && pKnownFile->GetAICHHashset()->HasValidMasterHash()
1633 && pKnownFile->GetAICHHashset()->GetMasterHash() == ahMasterHash && pKnownFile->GetPartCount() > nPart
1634 && pKnownFile->GetFileSize() > EMBLOCKSIZE && pKnownFile->GetFileSize() - PARTSIZE*nPart > EMBLOCKSIZE)
1636 CMemFile fileResponse;
1637 fileResponse.WriteHash(pKnownFile->GetFileHash());
1638 fileResponse.WriteUInt16(nPart);
1639 pKnownFile->GetAICHHashset()->GetMasterHash().Write(&fileResponse);
1640 if (pKnownFile->GetAICHHashset()->CreatePartRecoveryData(nPart*PARTSIZE, &fileResponse)){
1641 AddDebugLogLineM(false, logAICHTransfer,
1642 CFormat(wxT("AICH Packet Request: Sucessfully created and send recoverydata for '%s' to %s"))
1643 % pKnownFile->GetFileName() % GetClientFullInfo());
1645 CPacket* packAnswer = new CPacket(fileResponse, OP_EMULEPROT, OP_AICHANSWER);
1646 theStats::AddUpOverheadOther(packAnswer->GetPacketSize());
1647 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1648 SafeSendPacket(packAnswer);
1649 return;
1650 } else {
1651 AddDebugLogLineM(false, logAICHTransfer,
1652 CFormat(wxT("AICH Packet Request: Failed to create recoverydata for '%s' to %s"))
1653 % pKnownFile->GetFileName() % GetClientFullInfo());
1655 } else {
1656 AddDebugLogLineM(false, logAICHTransfer,
1657 CFormat(wxT("AICH Packet Request: Failed to create recoverydata - Hashset not ready or requested Hash differs from Masterhash for '%s' to %s"))
1658 % pKnownFile->GetFileName() % GetClientFullInfo());
1660 } else {
1661 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Request: Failed to find requested shared file - ") + GetClientFullInfo() );
1664 CPacket* packAnswer = new CPacket(OP_AICHANSWER, 16, OP_EMULEPROT);
1665 packAnswer->Copy16ToDataBuffer(hash.GetHash());
1666 theStats::AddUpOverheadOther(packAnswer->GetPacketSize());
1667 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1668 SafeSendPacket(packAnswer);
1671 void CUpDownClient::ProcessAICHFileHash(CMemFile* data, const CPartFile* file){
1672 CPartFile* pPartFile;
1673 if (file == NULL){
1674 pPartFile = theApp->downloadqueue->GetFileByID(data->ReadHash());
1675 } else {
1676 pPartFile = (CPartFile*)file;
1678 CAICHHash ahMasterHash(data);
1680 if(pPartFile != NULL && pPartFile == GetRequestFile()){
1681 SetReqFileAICHHash(new CAICHHash(ahMasterHash));
1682 pPartFile->GetAICHHashset()->UntrustedHashReceived(ahMasterHash, GetConnectIP());
1683 } else {
1684 AddDebugLogLineM( false, logAICHTransfer, wxT("ProcessAICHFileHash(): PartFile not found or Partfile differs from requested file, ") + GetClientFullInfo() );
1687 // File_checked_for_headers