Upstream tarball 20080609
[amule.git] / src / DownloadClient.cpp
blobdf2552bf878d1e8fd0330110272b9c1e8cc113e9
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 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 wxASSERT(0);
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*PARTSIZE,((done+1)*PARTSIZE)-1)){
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 wxASSERT(0);
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 theStats::AddDownloadingSource();
535 } else if (m_nDownloadState == DS_DOWNLOADING) {
536 theStats::RemoveDownloadingSource();
539 if (m_nDownloadState == DS_DOWNLOADING) {
540 m_nDownloadState = byNewState;
541 ClearDownloadBlockRequests();
543 kBpsDown = 0.0;
544 bytesReceivedCycle = 0;
545 msReceivedPrev = 0;
546 if (byNewState == DS_NONE) {
547 if (m_reqfile) {
548 m_reqfile->UpdatePartsFrequency( this, false ); // Decrement
550 m_downPartStatus.clear();
551 m_nPartCount = 0;
553 if (m_socket && byNewState != DS_ERROR) {
554 m_socket->DisableDownloadLimit();
557 m_nDownloadState = byNewState;
558 if(GetDownloadState() == DS_DOWNLOADING) {
559 if (IsEmuleClient()) {
560 SetRemoteQueueFull(false);
562 SetRemoteQueueRank(0); // eMule 0.30c set like this ...
564 UpdateDisplayedInfo(true);
567 /* eMule 0.30c implementation, i give it a try (Creteil) END ... */
569 void CUpDownClient::ProcessHashSet(const byte* packet, uint32 size)
571 if ((!m_reqfile) || md4cmp(packet,m_reqfile->GetFileHash().GetHash())) {
572 throw wxString(wxT("Wrong fileid sent (ProcessHashSet)"));
574 if (!m_fHashsetRequesting) {
575 throw wxString(wxT("Received unsolicited hashset, ignoring it."));
577 CMemFile data(packet,size);
578 if (m_reqfile->LoadHashsetFromFile(&data,true)) {
579 m_fHashsetRequesting = 0;
580 } else {
581 m_reqfile->SetHashSetNeeded(true);
582 throw wxString(wxT("Corrupted or invalid hashset received"));
584 SendStartupLoadReq();
587 void CUpDownClient::SendBlockRequests()
589 uint32 current_time = ::GetTickCount();
590 if (GetVBTTags()) {
592 // Ask new blocks only when all completed
593 if (m_PendingBlocks_list.size()) {
594 return;
597 if ((m_dwLastBlockReceived + SEC2MS(5)) > current_time) {
598 // We received last block in less than 5 secs? Let's request faster.
599 m_MaxBlockRequests = m_MaxBlockRequests << 1;
600 if ( m_MaxBlockRequests > 0x20) {
601 m_MaxBlockRequests = 0x20;
603 } else {
604 m_MaxBlockRequests = m_MaxBlockRequests >> 1;
605 if ( m_MaxBlockRequests < STANDARD_BLOCKS_REQUEST) {
606 m_MaxBlockRequests = STANDARD_BLOCKS_REQUEST;
611 m_dwLastBlockReceived = current_time;
613 if (!m_reqfile) {
614 return;
617 uint8 version = GetVBTTags() ? 2 : 1;
619 if (m_DownloadBlocks_list.empty()) {
620 // Barry - instead of getting 3, just get how many is needed
621 uint16 count = m_MaxBlockRequests - m_PendingBlocks_list.size();
622 std::vector<Requested_Block_Struct*> toadd(count);
623 if (m_reqfile->GetNextRequestedBlock(this,&(toadd[0]),&count)) {
624 for (int i = 0; i != count; i++) {
625 m_DownloadBlocks_list.push_back(toadd[i]);
630 // Barry - Why are unfinished blocks requested again, not just new ones?
632 while (m_PendingBlocks_list.size() < m_MaxBlockRequests && !m_DownloadBlocks_list.empty()) {
633 Pending_Block_Struct* pblock = new Pending_Block_Struct;
634 pblock->block = m_DownloadBlocks_list.front();
635 pblock->zStream = NULL;
636 pblock->totalUnzipped = 0;
637 pblock->fZStreamError = 0;
638 pblock->fRecovered = 0;
639 m_PendingBlocks_list.push_back(pblock);
640 m_DownloadBlocks_list.pop_front();
644 if (m_PendingBlocks_list.empty()) {
646 CUpDownClient* slower_client = NULL;
648 if (thePrefs::GetDropSlowSources()) {
649 slower_client = m_reqfile->GetSlowerDownloadingClient(m_lastaverage, this);
652 if (slower_client == NULL) {
653 slower_client = this;
656 if (!slower_client->GetSentCancelTransfer()) {
657 CPacket* packet = new CPacket(OP_CANCELTRANSFER, 0, OP_EDONKEYPROT);
658 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
659 if (slower_client != this) {
660 // printf("Dropped client %p to allow client %p to download\n",slower_client, this);
662 slower_client->ClearDownloadBlockRequests();
663 slower_client->SendPacket(packet,true,true);
664 slower_client->SetSentCancelTransfer(1);
667 slower_client->SetDownloadState(DS_NONEEDEDPARTS);
669 if (slower_client != this) {
670 // Re-request freed blocks.
671 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_CANCELTRANSFER (faster source eager to transfer) to ") + slower_client->GetFullIP() );
672 wxASSERT(m_DownloadBlocks_list.empty());
673 wxASSERT(m_PendingBlocks_list.empty());
674 uint16 count = m_MaxBlockRequests;
675 std::vector<Requested_Block_Struct*> toadd(count);
676 if (m_reqfile->GetNextRequestedBlock(this, &(toadd[0]),&count)) {
677 for (int i = 0; i != count; i++) {
678 Pending_Block_Struct* pblock = new Pending_Block_Struct;
679 pblock->block = toadd[i];
680 pblock->zStream = NULL;
681 pblock->totalUnzipped = 0;
682 pblock->fZStreamError = 0;
683 pblock->fRecovered = 0;
684 m_PendingBlocks_list.push_back(pblock);
686 } else {
687 // WTF, we just freed blocks.
688 wxASSERT(0);
689 return;
691 } else {
692 // Drop this one.
693 AddDebugLogLineM( false, 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 switch (version) {
702 case 2: {
703 // ED2Kv2 packet...
704 // Most common scenario: hash + blocks to request + every one
705 // having 2 uint32 tags
707 uint8 nBlocks = m_PendingBlocks_list.size();
708 if (nBlocks > m_MaxBlockRequests) {
709 nBlocks = m_MaxBlockRequests;
712 CMemFile data(16 + 1 + nBlocks*((2+4)*2));
714 data.WriteHash(m_reqfile->GetFileHash());
716 data.WriteUInt8(nBlocks);
718 std::list<Pending_Block_Struct*>::iterator it = m_PendingBlocks_list.begin();
719 while (nBlocks) {
720 wxASSERT(it != m_PendingBlocks_list.end());
721 wxASSERT( (*it)->block->StartOffset <= (*it)->block->EndOffset );
722 (*it)->fZStreamError = 0;
723 (*it)->fRecovered = 0;
724 CTagVarInt(/*Noname*/0,(*it)->block->StartOffset).WriteTagToFile(&data);
725 CTagVarInt(/*Noname*/0,(*it)->block->EndOffset).WriteTagToFile(&data);
726 ++it;
727 nBlocks--;
730 packet = new CPacket(data, OP_ED2KV2HEADER, OP_REQUESTPARTS);
731 AddDebugLogLineM( false, logLocalClient, wxString::Format(wxT("Local Client ED2Kv2: OP_REQUESTPARTS(%i) to "),(m_PendingBlocks_list.size()<m_MaxBlockRequests) ? m_PendingBlocks_list.size() : m_MaxBlockRequests) + GetFullIP() );
733 break;
735 case 1: {
736 wxASSERT(m_MaxBlockRequests == STANDARD_BLOCKS_REQUEST);
738 //#warning Kry - I dont specially like this approach, we iterate one time too many
740 bool bHasLongBlocks = false;
742 std::list<Pending_Block_Struct*>::iterator it = m_PendingBlocks_list.begin();
743 for (uint32 i = 0; i != m_MaxBlockRequests; i++){
744 if (it != m_PendingBlocks_list.end()) {
745 Pending_Block_Struct* pending = *it++;
746 wxASSERT( pending->block->StartOffset <= pending->block->EndOffset );
747 if (pending->block->StartOffset > 0xFFFFFFFF || pending->block->EndOffset > 0xFFFFFFFF){
748 bHasLongBlocks = true;
749 if (!SupportsLargeFiles()){
750 // Requesting a large block from a client that doesn't support large files?
751 wxASSERT( false );
752 if (!GetSentCancelTransfer()){
753 CPacket* cancel_packet = new CPacket(OP_CANCELTRANSFER, 0, OP_EDONKEYPROT);
754 theStats::AddUpOverheadFileRequest(cancel_packet->GetPacketSize());
755 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
756 SendPacket(cancel_packet,true,true);
757 SetSentCancelTransfer(1);
759 SetDownloadState(DS_ERROR);
761 break;
766 CMemFile data(16 /*Hash*/ + (m_MaxBlockRequests*(bHasLongBlocks ? 8 : 4) /* uint32/64 start*/) + (3*(bHasLongBlocks ? 8 : 4)/* uint32/64 end*/));
767 data.WriteHash(m_reqfile->GetFileHash());
769 it = m_PendingBlocks_list.begin();
770 for (uint32 i = 0; i != m_MaxBlockRequests; i++) {
771 if (it != m_PendingBlocks_list.end()) {
772 Pending_Block_Struct* pending = *it++;
773 wxASSERT( pending->block->StartOffset <= pending->block->EndOffset );
774 pending->fZStreamError = 0;
775 pending->fRecovered = 0;
776 if (bHasLongBlocks) {
777 data.WriteUInt64(pending->block->StartOffset);
778 } else {
779 data.WriteUInt32(pending->block->StartOffset);
781 } else {
782 if (bHasLongBlocks) {
783 data.WriteUInt64(0);
784 } else {
785 data.WriteUInt32(0);
790 it = m_PendingBlocks_list.begin();
791 for (uint32 i = 0; i != m_MaxBlockRequests; i++) {
792 if (it != m_PendingBlocks_list.end()) {
793 Requested_Block_Struct* block = (*it++)->block;
794 if (bHasLongBlocks) {
795 data.WriteUInt64(block->EndOffset+1);
796 } else {
797 data.WriteUInt32(block->EndOffset+1);
799 } else {
800 if (bHasLongBlocks) {
801 data.WriteUInt64(0);
802 } else {
803 data.WriteUInt32(0);
807 packet = new CPacket(data, (bHasLongBlocks ? OP_EMULEPROT : OP_EDONKEYPROT), (bHasLongBlocks ? (uint8)OP_REQUESTPARTS_I64 : (uint8)OP_REQUESTPARTS));
808 AddDebugLogLineM( false, logLocalClient, wxString::Format(wxT("Local Client: %s to "),(bHasLongBlocks ? wxT("OP_REQUESTPARTS_I64") : wxT("OP_REQUESTPARTS"))) + GetFullIP() );
809 break;
811 default:
812 wxASSERT(0);
815 if (packet) {
816 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
817 SendPacket(packet, true, true);
818 } else {
819 wxASSERT(0);
824 Barry - Originally this only wrote to disk when a full 180k block
825 had been received from a client, and only asked for data in
826 180k blocks.
828 This meant that on average 90k was lost for every connection
829 to a client data source. That is a lot of wasted data.
831 To reduce the lost data, packets are now written to a buffer
832 and flushed to disk regularly regardless of size downloaded.
834 This includes compressed packets.
836 Data is also requested only where gaps are, not in 180k blocks.
837 The requests will still not exceed 180k, but may be smaller to
838 fill a gap.
841 void CUpDownClient::ProcessBlockPacket(const byte* packet, uint32 size, bool packed, bool largeblocks)
843 // Ignore if no data required
844 if (!(GetDownloadState() == DS_DOWNLOADING || GetDownloadState() == DS_NONEEDEDPARTS)) {
845 return;
848 // This vars are defined here to be able to use them on the catch
849 int header_size = 16;
850 uint64 nStartPos = 0;
851 uint64 nEndPos = 0;
852 uint32 nBlockSize = 0;
853 uint32 lenUnzipped = 0;
855 // Update stats
856 m_dwLastBlockReceived = ::GetTickCount();
858 try {
860 // Read data from packet
861 const CMemFile data(packet, size);
863 // Check that this data is for the correct file
864 if ((!m_reqfile) || data.ReadHash() != m_reqfile->GetFileHash()) {
865 throw wxString(wxT("Wrong fileid sent (ProcessBlockPacket)"));
868 // Find the start & end positions, and size of this chunk of data
870 if (largeblocks) {
871 nStartPos = data.ReadUInt64();
872 header_size += 8;
873 } else {
874 nStartPos = data.ReadUInt32();
875 header_size += 4;
878 if (packed) {
879 nBlockSize = data.ReadUInt32();
880 header_size += 4;
881 nEndPos = nStartPos + (size - header_size);
882 } else {
883 if (largeblocks) {
884 nEndPos = data.ReadUInt64();
885 header_size += 8;
886 } else {
887 nEndPos = data.ReadUInt32();
888 header_size += 4;
892 // Check that packet size matches the declared data size + header size
893 if ( nEndPos == nStartPos || size != ((nEndPos - nStartPos) + header_size)) {
894 throw wxString(wxT("Corrupted or invalid DataBlock received (ProcessBlockPacket)"));
896 theStats::AddDownloadFromSoft(GetClientSoft(),size - header_size);
897 bytesReceivedCycle += size - header_size;
899 credits->AddDownloaded(size - header_size, GetIP(), theApp->CryptoAvailable());
901 // Move end back one, should be inclusive
902 nEndPos--;
904 // Loop through to find the reserved block that this is within
905 std::list<Pending_Block_Struct*>::iterator it = m_PendingBlocks_list.begin();
906 for (; it != m_PendingBlocks_list.end(); ++it) {
907 Pending_Block_Struct* cur_block = *it;
909 if ((cur_block->block->StartOffset <= nStartPos) && (cur_block->block->EndOffset >= nStartPos)) {
910 // Found reserved block
912 if (cur_block->block->StartOffset == nStartPos) {
913 // This block just started transfering. Set the start time.
914 m_last_block_start = ::GetTickCountFullRes();
917 if (cur_block->fZStreamError){
918 AddDebugLogLineM( false, logZLib,
919 CFormat(wxT("Ignoring %u bytes of block %u-%u because of erroneous zstream state for file: %s"))
920 % (size - header_size) % nStartPos % nEndPos % m_reqfile->GetFileName());
921 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
922 return;
925 // Remember this start pos, used to draw part downloading in list
926 m_nLastBlockOffset = nStartPos;
928 // Occasionally packets are duplicated, no point writing it twice
929 // This will be 0 in these cases, or the length written otherwise
930 uint32 lenWritten = 0;
932 // Handle differently depending on whether packed or not
933 if (!packed) {
934 // Write to disk (will be buffered in part file class)
935 lenWritten = m_reqfile->WriteToBuffer( size - header_size,
936 (byte*)(packet + header_size),
937 nStartPos,
938 nEndPos,
939 cur_block->block );
940 } else {
941 // Packed
942 wxASSERT( (long int)size > 0 );
943 // Create space to store unzipped data, the size is
944 // only an initial guess, will be resized in unzip()
945 // if not big enough
946 lenUnzipped = (size * 2);
947 // Don't get too big
948 if (lenUnzipped > (BLOCKSIZE + 300)) {
949 lenUnzipped = (BLOCKSIZE + 300);
951 byte *unzipped = new byte[lenUnzipped];
953 // Try to unzip the packet
954 int result = unzip(cur_block, (byte*)(packet + header_size), (size - header_size), &unzipped, &lenUnzipped);
956 // no block can be uncompressed to >2GB, 'lenUnzipped' is obviously erroneous.
957 if (result == Z_OK && ((int)lenUnzipped >= 0)) {
959 // Write any unzipped data to disk
960 if (lenUnzipped > 0) {
961 wxASSERT( (int)lenUnzipped > 0 );
963 // Use the current start and end positions for the uncompressed data
964 nStartPos = cur_block->block->StartOffset + cur_block->totalUnzipped - lenUnzipped;
965 nEndPos = cur_block->block->StartOffset + cur_block->totalUnzipped - 1;
967 if (nStartPos > cur_block->block->EndOffset || nEndPos > cur_block->block->EndOffset) {
968 AddDebugLogLineM( false, logZLib,
969 CFormat(wxT("Corrupted compressed packet for '%s' received (error 666)")) % m_reqfile->GetFileName());
970 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
971 } else {
972 // Write uncompressed data to file
973 lenWritten = m_reqfile->WriteToBuffer( size - header_size,
974 unzipped,
975 nStartPos,
976 nEndPos,
977 cur_block->block );
980 } else {
981 wxString strZipError;
982 if (cur_block->zStream && cur_block->zStream->msg) {
983 strZipError = wxT(" - ") + wxString::FromAscii(cur_block->zStream->msg);
986 AddDebugLogLineM( false, logZLib,
987 CFormat(wxT("Corrupted compressed packet for '%s' received (error %i): %s"))
988 % m_reqfile->GetFileName() % result % strZipError);
990 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
992 // If we had an zstream error, there is no chance that we could recover from it nor that we
993 // could use the current zstream (which is in error state) any longer.
994 if (cur_block->zStream){
995 inflateEnd(cur_block->zStream);
996 delete cur_block->zStream;
997 cur_block->zStream = NULL;
1000 // Although we can't further use the current zstream, there is no need to disconnect the sending
1001 // client because the next zstream (a series of 10K-blocks which build a 180K-block) could be
1002 // valid again. Just ignore all further blocks for the current zstream.
1003 cur_block->fZStreamError = 1;
1004 cur_block->totalUnzipped = 0; // bluecow's fix
1006 delete [] unzipped;
1008 // These checks only need to be done if any data was written
1009 if (lenWritten > 0) {
1010 m_nTransferredDown += lenWritten;
1012 // If finished reserved block
1013 if (nEndPos == cur_block->block->EndOffset) {
1015 // Save last average speed based on data and time.
1016 // This should do bytes/sec.
1017 uint32 average_time = (::GetTickCountFullRes() - m_last_block_start);
1019 // Avoid divide by 0.
1020 if (average_time == 0) {
1021 average_time++;
1024 m_lastaverage = ((cur_block->block->EndOffset - cur_block->block->StartOffset) * 1000) / average_time;
1026 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
1027 delete cur_block->block;
1028 // Not always allocated
1029 if (cur_block->zStream) {
1030 inflateEnd(cur_block->zStream);
1031 delete cur_block->zStream;
1033 delete cur_block;
1034 m_PendingBlocks_list.erase(it);
1036 // Request next block
1037 SendBlockRequests();
1040 // Stop looping and exit method
1041 return;
1044 } catch (const CEOFException& e) {
1045 wxString error = wxString(wxT("Error reading "));
1046 if (packed) error += wxString::Format(wxT("packed (LU: %i) "),lenUnzipped);
1047 if (packed) error += wxT("largeblocks ");
1048 error += wxString::Format(wxT("data packet: RS: %i HS: %i SP: %i EP: %i BS: %i -> "),size,header_size,nStartPos,nEndPos,nBlockSize);
1049 AddDebugLogLineM(true, logRemoteClient, error + e.what());
1050 return;
1054 int CUpDownClient::unzip(Pending_Block_Struct *block, byte *zipped, uint32 lenZipped, byte **unzipped, uint32 *lenUnzipped, int iRecursion)
1056 int err = Z_DATA_ERROR;
1058 // Save some typing
1059 z_stream *zS = block->zStream;
1061 // Is this the first time this block has been unzipped
1062 if (zS == NULL) {
1063 // Create stream
1064 block->zStream = new z_stream;
1065 zS = block->zStream;
1067 // Initialise stream values
1068 zS->zalloc = (alloc_func)0;
1069 zS->zfree = (free_func)0;
1070 zS->opaque = (voidpf)0;
1072 // Set output data streams, do this here to avoid overwriting on recursive calls
1073 zS->next_out = (*unzipped);
1074 zS->avail_out = (*lenUnzipped);
1076 // Initialise the z_stream
1077 err = inflateInit(zS);
1078 if (err != Z_OK) {
1079 return err;
1083 // Use whatever input is provided
1084 zS->next_in = zipped;
1085 zS->avail_in = lenZipped;
1087 // Only set the output if not being called recursively
1088 if (iRecursion == 0) {
1089 zS->next_out = (*unzipped);
1090 zS->avail_out = (*lenUnzipped);
1093 // Try to unzip the data
1094 err = inflate(zS, Z_SYNC_FLUSH);
1096 // Is zip finished reading all currently available input and writing
1097 // all generated output
1098 if (err == Z_STREAM_END) {
1099 // Finish up
1100 err = inflateEnd(zS);
1101 if (err != Z_OK) {
1102 return err;
1105 // Got a good result, set the size to the amount unzipped in this call
1106 // (including all recursive calls)
1107 (*lenUnzipped) = (zS->total_out - block->totalUnzipped);
1108 block->totalUnzipped = zS->total_out;
1109 } else if ((err == Z_OK) && (zS->avail_out == 0) && (zS->avail_in != 0)) {
1111 // Output array was not big enough,
1112 // call recursively until there is enough space
1114 // What size should we try next
1115 uint32 newLength = (*lenUnzipped) *= 2;
1116 if (newLength == 0) {
1117 newLength = lenZipped * 2;
1119 // Copy any data that was successfully unzipped to new array
1120 byte *temp = new byte[newLength];
1121 wxASSERT( zS->total_out - block->totalUnzipped <= newLength );
1122 memcpy(temp, (*unzipped), (zS->total_out - block->totalUnzipped));
1123 delete [] (*unzipped);
1124 (*unzipped) = temp;
1125 (*lenUnzipped) = newLength;
1127 // Position stream output to correct place in new array
1128 zS->next_out = (*unzipped) + (zS->total_out - block->totalUnzipped);
1129 zS->avail_out = (*lenUnzipped) - (zS->total_out - block->totalUnzipped);
1131 // Try again
1132 err = unzip(block, zS->next_in, zS->avail_in, unzipped, lenUnzipped, iRecursion + 1);
1133 } else if ((err == Z_OK) && (zS->avail_in == 0)) {
1134 // All available input has been processed, everything ok.
1135 // Set the size to the amount unzipped in this call
1136 // (including all recursive calls)
1137 (*lenUnzipped) = (zS->total_out - block->totalUnzipped);
1138 block->totalUnzipped = zS->total_out;
1139 } else {
1140 // Should not get here unless input data is corrupt
1141 wxString strZipError;
1143 if ( zS->msg ) {
1144 strZipError = wxString::Format(wxT(" %d '"), err) + wxString::FromAscii(zS->msg) + wxT("'");
1145 } else if (err != Z_OK) {
1146 strZipError = wxString::Format(wxT(" %d"), err);
1149 AddDebugLogLineM(false, logZLib,
1150 CFormat(wxT("Unexpected zip error %s in file '%s'"))
1151 % strZipError % (m_reqfile ? m_reqfile->GetFileName() : CPath(wxT("?"))));
1154 if (err != Z_OK) {
1155 (*lenUnzipped) = 0;
1158 return err;
1162 // Emilio: rewrite of eMule code to eliminate use of lists for averaging and fix
1163 // errors in calculation (32-bit rollover and time measurement) This function
1164 // uses a first-order filter with variable time constant (initially very short
1165 // to quickly reach the right value without spiking, then gradually approaching
1166 // the value of 50 seconds which is equivalent to the original averaging period
1167 // used in eMule). The download rate is measured using actual timestamps. The
1168 // filter-based averaging however uses a simplified algorithm that assumes a
1169 // fixed loop time - this does not introduce any measurement error, it simply
1170 // makes the degree of smoothing slightly imprecise (the true TC of the filter
1171 // varies inversely with the true loop time), which is of no importance here.
1173 float CUpDownClient::CalculateKBpsDown()
1175 // -- all timing values are in seconds --
1176 const float tcLoop = 0.1f; // _assumed_ Process() loop time = 0.1 sec
1177 const float tcInit = 0.4f; // initial filter time constant
1178 const float tcFinal = 50.0f; // final filter time constant
1179 const float tcReduce = 5.0f; // transition from tcInit to tcFinal
1181 const float fInit = tcLoop/tcInit; // initial averaging factor
1182 const float fFinal = tcLoop/tcFinal; // final averaging factor
1183 const float fReduce = std::exp(std::log(fFinal/fInit) / (tcReduce/tcLoop)) * 0.99999;
1185 uint32 msCur = ::GetTickCount();
1187 if (msReceivedPrev == 0) { // initialize the averaging filter
1188 fDownAvgFilter = fInit;
1189 // "kBpsDown = bytesReceivedCycle/1024.0 / tcLoop" would be technically correct,
1190 // but the first loop often receives a large chunk of data and then produces a spike
1191 kBpsDown = /* 0.0 * (1.0-fInit) + */ bytesReceivedCycle/1024.0 / tcLoop * fInit;
1192 bytesReceivedCycle = 0;
1193 } else if (msCur != msReceivedPrev) { // (safeguard against divide-by-zero)
1194 if (fDownAvgFilter > fFinal) { // reduce time constant during ramp-up phase
1195 fDownAvgFilter *= fReduce; // this approximates averaging a lengthening list
1197 kBpsDown = kBpsDown * (1.0 - fDownAvgFilter)
1198 + (bytesReceivedCycle/1.024)/((float)(msCur-msReceivedPrev)) * fDownAvgFilter;
1199 bytesReceivedCycle = 0;
1201 msReceivedPrev = msCur;
1203 m_cShowDR++;
1204 if (m_cShowDR == 30){
1205 m_cShowDR = 0;
1206 UpdateDisplayedInfo();
1208 if ((::GetTickCount() - m_dwLastBlockReceived) > DOWNLOADTIMEOUT){
1209 if (!GetSentCancelTransfer()){
1210 CPacket* packet = new CPacket(OP_CANCELTRANSFER, 0, OP_EDONKEYPROT);
1211 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
1212 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
1213 SendPacket(packet,true,true);
1214 SetSentCancelTransfer(1);
1216 SetDownloadState(DS_ONQUEUE);
1219 return kBpsDown;
1222 uint16 CUpDownClient::GetAvailablePartCount() const
1224 uint16 result = 0;
1225 for (int i = 0;i != m_nPartCount;i++){
1226 if (IsPartAvailable(i))
1227 result++;
1229 return result;
1232 void CUpDownClient::SetRemoteQueueRank(uint16 nr)
1234 m_nOldRemoteQueueRank = m_nRemoteQueueRank;
1235 m_nRemoteQueueRank = nr;
1236 UpdateDisplayedInfo();
1239 void CUpDownClient::UDPReaskACK(uint16 nNewQR)
1241 // 0.42e
1242 m_bUDPPending = false;
1243 SetRemoteQueueRank(nNewQR);
1244 m_dwLastAskedTime = ::GetTickCount();
1247 void CUpDownClient::UDPReaskFNF()
1249 m_bUDPPending = false;
1251 // avoid premature deletion of 'this' client
1252 if (GetDownloadState() != DS_DOWNLOADING){
1253 if (m_reqfile) {
1254 m_reqfile->AddDeadSource(this);
1257 theApp->downloadqueue->RemoveSource(this);
1258 if (!m_socket) {
1259 if (Disconnected(wxT("UDPReaskFNF m_socket=NULL"))) {
1260 Safe_Delete();
1263 } else {
1264 AddDebugLogLineM( false, logRemoteClient, wxT("UDP ANSWER FNF : ") + GetUserName() + wxT(" - did not remove client because of current download state") );
1268 void CUpDownClient::UDPReaskForDownload()
1271 wxASSERT(m_reqfile);
1273 if(!m_reqfile || m_bUDPPending ) {
1274 return;
1277 //#warning We should implement the quality tests for udp reliability
1279 if( m_nTotalUDPPackets > 3 && ((float)(m_nFailedUDPPackets/m_nTotalUDPPackets) > .3)) {
1280 return;
1284 if (thePrefs::GetEffectiveUDPPort() == 0) {
1285 return;
1288 if (m_nUDPPort != 0 && !theApp->IsFirewalled() && !IsConnected()) {
1289 //don't use udp to ask for sources
1290 if(IsSourceRequestAllowed()) {
1291 return;
1294 m_bUDPPending = true;
1296 CMemFile data(128);
1297 data.WriteHash(m_reqfile->GetFileHash());
1299 if (GetUDPVersion() > 3) {
1300 if (m_reqfile->IsPartFile()) {
1301 ((CPartFile*)m_reqfile)->WritePartStatus(&data);
1303 else {
1304 data.WriteUInt16(0);
1308 if (GetUDPVersion() > 2) {
1309 data.WriteUInt16(m_reqfile->m_nCompleteSourcesCount);
1312 CPacket* response = new CPacket(data, OP_EMULEPROT, OP_REASKFILEPING);
1313 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket: send OP_REASKFILEPING") );
1314 theStats::AddUpOverheadFileRequest(response->GetPacketSize());
1315 theApp->clientudp->SendPacket(response,GetConnectIP(),GetUDPPort(), ShouldReceiveCryptUDPPackets(), GetUserHash().GetHash(), false, 0);
1316 } else if (HasLowID() && GetBuddyIP() && GetBuddyPort() && HasValidBuddyID()) {
1318 m_bUDPPending = true;
1320 CMemFile data(128);
1322 data.WriteHash(CMD4Hash(GetBuddyID()));
1323 data.WriteHash(m_reqfile->GetFileHash());
1325 if (GetUDPVersion() > 3) {
1326 if (m_reqfile->IsPartFile()) {
1327 ((CPartFile*)m_reqfile)->WritePartStatus(&data);
1328 } else {
1329 data.WriteUInt16(0);
1333 if (GetUDPVersion() > 2) {
1334 data.WriteUInt16(m_reqfile->m_nCompleteSourcesCount);
1337 CPacket* response = new CPacket(data, OP_EMULEPROT, OP_REASKCALLBACKUDP);
1338 AddDebugLogLineM( false, logClientUDP, wxT("Client UDP socket: send OP_REASKCALLBACKUDP") );
1339 theStats::AddUpOverheadFileRequest(response->GetPacketSize());
1340 theApp->clientudp->SendPacket(response, GetBuddyIP(), GetBuddyPort(), false, NULL, true, 0 );
1345 //! Barry - Sets string to show parts downloading, eg NNNYNNNNYYNYN
1346 wxString CUpDownClient::ShowDownloadingParts() const
1348 // Initialise to all N's
1349 wxString Parts(wxT('N'), m_nPartCount);
1351 std::list<Pending_Block_Struct*>::const_iterator it = m_PendingBlocks_list.begin();
1352 for (; it != m_PendingBlocks_list.end(); ++it) {
1353 Parts.SetChar(((*it)->block->StartOffset / PARTSIZE), 'Y');
1356 return Parts;
1360 void CUpDownClient::UpdateDisplayedInfo(bool force)
1362 uint32 curTick = ::GetTickCount();
1363 if(force || curTick-m_lastRefreshedDLDisplay > MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE) {
1364 // Check if we actually need to notify of changes
1365 bool update = m_reqfile && m_reqfile->ShowSources();
1367 // Check A4AF files only if needed
1368 if ( !update ) {
1369 A4AFList::iterator it = m_A4AF_list.begin();
1370 for ( ; it != m_A4AF_list.end(); ++it ) {
1371 if ( it->first->ShowSources() ) {
1372 update = true;
1373 break;
1378 // And finnaly trigger an event if there's any reason
1379 if ( update ) {
1380 Notify_DownloadCtrlUpdateItem(this);
1383 m_lastRefreshedDLDisplay = curTick;
1388 // IgnoreNoNeeded = will switch to files of which this source has no needed parts (if no better fiels found)
1389 // ignoreSuspensions = ignore timelimit for A4Af jumping
1390 // bRemoveCompletely = do not readd the file which the source is swapped from to the A4AF lists (needed if deleting or stopping a file)
1391 // toFile = Try to swap to this partfile only
1393 bool CUpDownClient::SwapToAnotherFile(bool bIgnoreNoNeeded, bool ignoreSuspensions, bool bRemoveCompletely, CPartFile* toFile)
1395 // Fail if m_reqfile is invalid
1396 if ( m_reqfile == NULL ) {
1397 return false;
1400 // It would be stupid to swap away a downloading source
1401 if (GetDownloadState() == DS_DOWNLOADING) {
1402 return false;
1405 // The iterator of the final target
1406 A4AFList::iterator target = m_A4AF_list.end();
1408 // Do we want to swap to a specific file?
1409 if ( toFile != NULL ) {
1410 A4AFList::iterator it = m_A4AF_list.find( toFile );
1411 if ( it != m_A4AF_list.end() ) {
1413 // We force ignoring of noneeded flag and timestamps
1414 if ( IsValidSwapTarget( it, true, true ) ) {
1415 // Set the target
1416 target = it;
1419 } else {
1420 // We want highest priority possible, but need to start with
1421 // a value less than any other priority
1422 char priority = -1;
1424 A4AFList::iterator it = m_A4AF_list.begin();
1425 for ( ; it != m_A4AF_list.end(); ++it ) {
1426 if ( IsValidSwapTarget( it, bIgnoreNoNeeded, ignoreSuspensions ) ) {
1427 char cur_priority = it->first->GetDownPriority();
1429 // We would prefer to get files with needed parts, thus rate them higher.
1430 // However, this really only matters if bIgnoreNoNeeded is true.
1431 if ( it->second.NeededParts )
1432 cur_priority += 10;
1434 // Change target if the current file has a higher rate than the previous
1435 if ( cur_priority > priority ) {
1436 priority = cur_priority;
1438 // Set the new target
1439 target = it;
1441 // Break on the first High-priority file with needed parts
1442 if ( priority == PR_HIGH + 10 ) {
1443 break;
1450 // Try to swap if we found a valid target
1451 if ( target != m_A4AF_list.end() ) {
1453 // Sainity check, if reqfile doesn't own the source, then something
1454 // is wrong and the swap cannot proceed.
1455 if ( m_reqfile->DelSource( this ) ) {
1456 CPartFile* SwapTo = target->first;
1458 // remove this client from the A4AF list of our new m_reqfile
1459 if ( SwapTo->RemoveA4AFSource( this ) ) {
1460 Notify_DownloadCtrlRemoveSource(this, SwapTo);
1463 m_reqfile->RemoveDownloadingSource( this );
1465 // Do we want to remove it completly? Say if the old file is getting deleted
1466 if ( !bRemoveCompletely ) {
1467 m_reqfile->AddA4AFSource( this );
1469 // Set the status of the old file
1470 m_A4AF_list[m_reqfile].NeededParts = (GetDownloadState() != DS_NONEEDEDPARTS);
1472 // Avoid swapping to this file for a while
1473 m_A4AF_list[m_reqfile].timestamp = ::GetTickCount();
1475 Notify_DownloadCtrlAddSource(m_reqfile, this, A4AF_SOURCE);
1476 } else {
1477 Notify_DownloadCtrlRemoveSource( this, m_reqfile );
1480 SetDownloadState(DS_NONE);
1481 ResetFileStatusInfo();
1483 m_nRemoteQueueRank = 0;
1484 m_nOldRemoteQueueRank = 0;
1486 m_reqfile->UpdatePartsInfo();
1488 SetRequestFile( SwapTo );
1490 SwapTo->AddSource( this );
1492 Notify_DownloadCtrlAddSource(SwapTo, this, UNAVAILABLE_SOURCE);
1494 // Remove the new reqfile from the list of other files
1495 m_A4AF_list.erase( target );
1497 return true;
1501 return false;
1505 bool CUpDownClient::IsValidSwapTarget( A4AFList::iterator it, bool ignorenoneeded, bool ignoresuspended )
1507 wxASSERT( it != m_A4AF_list.end() && it->first );
1509 // Check if this file has been suspended
1510 if ( !ignoresuspended ) {
1511 if ( ::GetTickCount() - it->second.timestamp >= PURGESOURCESWAPSTOP ) {
1512 // The wait-time has been exceeded and the file is now a valid target
1513 it->second.timestamp = 0;
1514 } else {
1515 // The file was still suspended and we are not ignoring suspensions
1516 return false;
1520 // Check if the client has needed parts
1521 if ( !ignorenoneeded ) {
1522 if ( !it->second.NeededParts ) {
1523 return false;
1527 // Final checks to see if the client is a valid target
1528 CPartFile* cur_file = it->first;
1529 if ( ( cur_file != m_reqfile && !cur_file->IsStopped() ) &&
1530 ( cur_file->GetStatus() == PS_READY || cur_file->GetStatus() == PS_EMPTY ) &&
1531 ( cur_file->IsPartFile() ) )
1533 return true;
1534 } else {
1535 return false;
1540 void CUpDownClient::SetRequestFile(CPartFile* reqfile)
1542 if ( m_reqfile != reqfile ) {
1543 // Decrement the source-count of the old request-file
1544 if ( m_reqfile ) {
1545 m_reqfile->ClientStateChanged( GetDownloadState(), -1 );
1546 m_reqfile->UpdatePartsFrequency( this, false );
1549 m_nPartCount = 0;
1550 m_downPartStatus.clear();
1552 m_reqfile = reqfile;
1554 if ( reqfile ) {
1555 // Increment the source-count of the new request-file
1556 m_reqfile->ClientStateChanged( -1, GetDownloadState() );
1558 m_nPartCount = reqfile->GetPartCount();
1563 void CUpDownClient::SetReqFileAICHHash(CAICHHash* val){
1564 if(m_pReqFileAICHHash != NULL && m_pReqFileAICHHash != val)
1565 delete m_pReqFileAICHHash;
1566 m_pReqFileAICHHash = val;
1569 void CUpDownClient::SendAICHRequest(CPartFile* pForFile, uint16 nPart){
1570 CAICHRequestedData request;
1571 request.m_nPart = nPart;
1572 request.m_pClient = this;
1573 request.m_pPartFile = pForFile;
1574 CAICHHashSet::m_liRequestedData.push_back(request);
1575 m_fAICHRequested = TRUE;
1576 CMemFile data;
1577 data.WriteHash(pForFile->GetFileHash());
1578 data.WriteUInt16(nPart);
1579 pForFile->GetAICHHashset()->GetMasterHash().Write(&data);
1580 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_AICHREQUEST);
1581 theStats::AddUpOverheadOther(packet->GetPacketSize());
1582 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_AICHREQUEST to") + GetFullIP());
1583 SafeSendPacket(packet);
1586 void CUpDownClient::ProcessAICHAnswer(const byte* packet, uint32 size)
1588 if (m_fAICHRequested == FALSE){
1589 throw wxString(wxT("Received unrequested AICH Packet"));
1591 m_fAICHRequested = FALSE;
1593 CMemFile data(packet, size);
1594 if (size <= 16){
1595 CAICHHashSet::ClientAICHRequestFailed(this);
1596 return;
1599 CMD4Hash hash = data.ReadHash();
1600 CPartFile* pPartFile = theApp->downloadqueue->GetFileByID(hash);
1601 CAICHRequestedData request = CAICHHashSet::GetAICHReqDetails(this);
1602 uint16 nPart = data.ReadUInt16();
1603 if (pPartFile != NULL && request.m_pPartFile == pPartFile && request.m_pClient == this && nPart == request.m_nPart){
1604 CAICHHash ahMasterHash(&data);
1605 if ( (pPartFile->GetAICHHashset()->GetStatus() == AICH_TRUSTED || pPartFile->GetAICHHashset()->GetStatus() == AICH_VERIFIED)
1606 && ahMasterHash == pPartFile->GetAICHHashset()->GetMasterHash())
1608 if(pPartFile->GetAICHHashset()->ReadRecoveryData(request.m_nPart*PARTSIZE, &data)){
1609 // finally all checks passed, everythings seem to be fine
1610 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1611 CAICHHashSet::RemoveClientAICHRequest(this);
1612 pPartFile->AICHRecoveryDataAvailable(request.m_nPart);
1613 return;
1614 } else {
1615 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1617 } else {
1618 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Answer: Masterhash differs from packethash or hashset has no trusted Masterhash") );
1620 } else {
1621 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Answer: requested values differ from values in packet") );
1624 CAICHHashSet::ClientAICHRequestFailed(this);
1628 void CUpDownClient::ProcessAICHRequest(const byte* packet, uint32 size)
1630 if (size != 16 + 2 + CAICHHash::GetHashSize()) {
1631 throw wxString(wxT("Received AICH Request Packet with wrong size"));
1634 CMemFile data(packet, size);
1636 CMD4Hash hash = data.ReadHash();
1637 uint16 nPart = data.ReadUInt16();
1638 CAICHHash ahMasterHash(&data);
1639 CKnownFile* pKnownFile = theApp->sharedfiles->GetFileByID(hash);
1640 if (pKnownFile != NULL){
1641 if (pKnownFile->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE && pKnownFile->GetAICHHashset()->HasValidMasterHash()
1642 && pKnownFile->GetAICHHashset()->GetMasterHash() == ahMasterHash && pKnownFile->GetPartCount() > nPart
1643 && pKnownFile->GetFileSize() > EMBLOCKSIZE && pKnownFile->GetFileSize() - PARTSIZE*nPart > EMBLOCKSIZE)
1645 CMemFile fileResponse;
1646 fileResponse.WriteHash(pKnownFile->GetFileHash());
1647 fileResponse.WriteUInt16(nPart);
1648 pKnownFile->GetAICHHashset()->GetMasterHash().Write(&fileResponse);
1649 if (pKnownFile->GetAICHHashset()->CreatePartRecoveryData(nPart*PARTSIZE, &fileResponse)){
1650 AddDebugLogLineM(false, logAICHTransfer,
1651 CFormat(wxT("AICH Packet Request: Sucessfully created and send recoverydata for '%s' to %s"))
1652 % pKnownFile->GetFileName() % GetClientFullInfo());
1654 CPacket* packAnswer = new CPacket(fileResponse, OP_EMULEPROT, OP_AICHANSWER);
1655 theStats::AddUpOverheadOther(packAnswer->GetPacketSize());
1656 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1657 SafeSendPacket(packAnswer);
1658 return;
1659 } else {
1660 AddDebugLogLineM(false, logAICHTransfer,
1661 CFormat(wxT("AICH Packet Request: Failed to create recoverydata for '%s' to %s"))
1662 % pKnownFile->GetFileName() % GetClientFullInfo());
1664 } else {
1665 AddDebugLogLineM(false, logAICHTransfer,
1666 CFormat(wxT("AICH Packet Request: Failed to create recoverydata - Hashset not ready or requested Hash differs from Masterhash for '%s' to %s"))
1667 % pKnownFile->GetFileName() % GetClientFullInfo());
1669 } else {
1670 AddDebugLogLineM( false, logAICHTransfer, wxT("AICH Packet Request: Failed to find requested shared file - ") + GetClientFullInfo() );
1673 CPacket* packAnswer = new CPacket(OP_AICHANSWER, 16, OP_EMULEPROT);
1674 packAnswer->Copy16ToDataBuffer(hash.GetHash());
1675 theStats::AddUpOverheadOther(packAnswer->GetPacketSize());
1676 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1677 SafeSendPacket(packAnswer);
1680 void CUpDownClient::ProcessAICHFileHash(CMemFile* data, const CPartFile* file){
1681 CPartFile* pPartFile;
1682 if (file == NULL){
1683 pPartFile = theApp->downloadqueue->GetFileByID(data->ReadHash());
1684 } else {
1685 pPartFile = (CPartFile*)file;
1687 CAICHHash ahMasterHash(data);
1689 if(pPartFile != NULL && pPartFile == GetRequestFile()){
1690 SetReqFileAICHHash(new CAICHHash(ahMasterHash));
1691 pPartFile->GetAICHHashset()->UntrustedHashReceived(ahMasterHash, GetConnectIP());
1692 } else {
1693 AddDebugLogLineM( false, logAICHTransfer, wxT("ProcessAICHFileHash(): PartFile not found or Partfile differs from requested file, ") + GetClientFullInfo() );
1696 // File_checked_for_headers