1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
10 //
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.
15 //
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
24 //
25
26 #include "updownclient.h" // Needed for CUpDownClient
27
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>
34
35 #include <zlib.h>
36 #include <cmath> // Needed for std:exp
37
38 #include "ClientCredits.h" // Needed for CClientCredits
39 #include "ClientUDPSocket.h" // Needed for CClientUDPSocket
40 #include "DownloadQueue.h" // Needed for CDownloadQueue
41 #include "Preferences.h" // Needed for thePrefs
42 #include "Packet.h" // Needed for CPacket
43 #include "MemFile.h" // Needed for CMemFile
44 #include "ClientTCPSocket.h"// Needed for CClientTCPSocket
45 #include "ListenSocket.h" // Needed for CListenSocket
46 #include "amule.h" // Needed for theApp
47 #include "PartFile.h" // Needed for CPartFile
48 #include "SharedFileList.h"
49 #include "Statistics.h" // Needed for theStats
50 #include "Logger.h"
51 #include "GuiEvents.h" // Needed for Notify_*
52 #include "UploadQueue.h" // Needed for CUploadQueue
53
54
55 #ifdef __MULE_UNUSED_CODE__
56 // This function is left as a reminder.
57 // Changes here _must_ be reflected in CClientList::FindMatchingClient.
Compare(const CUpDownClient * tocomp,bool bIgnoreUserhash) const58 bool CUpDownClient::Compare(const CUpDownClient* tocomp, bool bIgnoreUserhash) const
59 {
60 if (!tocomp) {
61 // should we wxASSERT here?
62 return false;
63 }
64
65 //Compare only the user hash..
66 if(!bIgnoreUserhash && HasValidHash() && tocomp->HasValidHash()) {
67 return GetUserHash() == tocomp->GetUserHash();
68 }
69
70 if (HasLowID()) {
71 //User is firewalled.. Must do two checks..
72 if (GetIP()!=0 && GetIP() == tocomp->GetIP()) {
73 //The IP of both match
74 if (GetUserPort()!=0 && GetUserPort() == tocomp->GetUserPort()) {
75 //IP-UserPort matches
76 return true;
77 }
78 if (GetKadPort()!=0 && GetKadPort() == tocomp->GetKadPort()) {
79 //IP-KadPort Matches
80 return true;
81 }
82 }
83
84 if (GetUserIDHybrid()!=0
85 && GetUserIDHybrid() == tocomp->GetUserIDHybrid()
86 && GetServerIP()!=0
87 && GetServerIP() == tocomp->GetServerIP()
88 && GetServerPort()!=0
89 && GetServerPort() == tocomp->GetServerPort()) {
90 //Both have the same lowID, Same serverIP and Port..
91 return true;
92 }
93
94 //Both IP, and Server do not match..
95 return false;
96 }
97
98 //User is not firewalled.
99 if (GetUserPort()!=0) {
100 //User has a Port, lets check the rest.
101 if (GetIP() != 0 && tocomp->GetIP() != 0) {
102 //Both clients have a verified IP..
103 if(GetIP() == tocomp->GetIP() && GetUserPort() == tocomp->GetUserPort()) {
104 //IP and UserPort match..
105 return true;
106 }
107 } else {
108 //One of the two clients do not have a verified IP
109 if (GetUserIDHybrid() == tocomp->GetUserIDHybrid() && GetUserPort() == tocomp->GetUserPort()) {
110 //ID and Port Match..
111 return true;
112 }
113 }
114 }
115
116 if(GetKadPort()!=0) {
117 //User has a Kad Port.
118 if(GetIP() != 0 && tocomp->GetIP() != 0) {
119 //Both clients have a verified IP.
120 if(GetIP() == tocomp->GetIP() && GetKadPort() == tocomp->GetKadPort()) {
121 //IP and KadPort Match..
122 return true;
123 }
124 } else {
125 //One of the users do not have a verified IP.
126 if (GetUserIDHybrid() == tocomp->GetUserIDHybrid() && GetKadPort() == tocomp->GetKadPort()) {
127 //ID and KadProt Match..
128 return true;
129 }
130 }
131 }
132
133 //No Matches..
134 return false;
135 }
136 #endif
137
138
AskForDownload()139 bool CUpDownClient::AskForDownload()
140 {
141 // 0.42e
142 if (theApp->listensocket->TooManySockets()) {
143 if (!m_socket) {
144 if (GetDownloadState() != DS_TOOMANYCONNS) {
145 SetDownloadState(DS_TOOMANYCONNS);
146 }
147 return true;
148 } else if (!m_socket->IsConnected()) {
149 if (GetDownloadState() != DS_TOOMANYCONNS) {
150 SetDownloadState(DS_TOOMANYCONNS);
151 }
152 return true;
153 }
154 }
155 m_bUDPPending = false;
156 m_dwLastAskedTime = ::GetTickCount();
157 SetDownloadState(DS_CONNECTING);
158 SetSentCancelTransfer(0);
159 return TryToConnect();
160 }
161
162
SendStartupLoadReq()163 void CUpDownClient::SendStartupLoadReq()
164 {
165 // 0.42e
166 if (m_socket==NULL || m_reqfile==NULL) {
167 return;
168 }
169 SetDownloadState(DS_ONQUEUE);
170 CMemFile dataStartupLoadReq(16);
171 dataStartupLoadReq.WriteHash(m_reqfile->GetFileHash());
172 CPacket* packet = new CPacket(dataStartupLoadReq, OP_EDONKEYPROT, OP_STARTUPLOADREQ);
173 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
174 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_STARTUPLOADREQ to ") + GetFullIP());
175 SendPacket(packet, true, true);
176 }
177
178
IsSourceRequestAllowed()179 bool CUpDownClient::IsSourceRequestAllowed()
180 {
181 //#warning REWRITE - Source swapping from eMule.
182 // 0.42e
183 uint32 dwTickCount = ::GetTickCount() + CONNECTION_LATENCY;
184 uint32 nTimePassedClient = dwTickCount - GetLastSrcAnswerTime();
185 uint32 nTimePassedFile = dwTickCount - m_reqfile->GetLastAnsweredTime();
186 bool bNeverAskedBefore = (GetLastAskedForSources() == 0);
187
188 uint32 uSources = m_reqfile->GetSourceCount();
189 return (
190 // if client has the correct extended protocol
191 ExtProtocolAvailable() && (SupportsSourceExchange2() || GetSourceExchange1Version() > 1) &&
192 // AND if we need more sources
193 thePrefs::GetMaxSourcePerFileSoft() > uSources &&
194 // AND if...
195 (
196 //source is not complete and file is very rare
197 ( !m_bCompleteSource
198 && (bNeverAskedBefore || nTimePassedClient > SOURCECLIENTREASKS)
199 && (uSources <= RARE_FILE/5)
200 ) ||
201 //source is not complete and file is rare
202 ( !m_bCompleteSource
203 && (bNeverAskedBefore || nTimePassedClient > SOURCECLIENTREASKS)
204 && (uSources <= RARE_FILE || uSources - m_reqfile->GetValidSourcesCount() <= RARE_FILE / 2)
205 && (nTimePassedFile > SOURCECLIENTREASKF)
206 ) ||
207 // OR if file is not rare
208 ( (bNeverAskedBefore || nTimePassedClient > (unsigned)(SOURCECLIENTREASKS * MINCOMMONPENALTY))
209 && (nTimePassedFile > (unsigned)(SOURCECLIENTREASKF * MINCOMMONPENALTY))
210 )
211 )
212 );
213 }
214
215
SendFileRequest()216 void CUpDownClient::SendFileRequest()
217 {
218 wxCHECK_RET(m_reqfile, wxT("Cannot request file when no reqfile is set"));
219
220 CMemFile dataFileReq(16+16);
221 dataFileReq.WriteHash(m_reqfile->GetFileHash());
222
223 if (SupportMultiPacket()) {
224 DEBUG_ONLY( wxString sent_opcodes; )
225
226 if (SupportExtMultiPacket()) {
227 dataFileReq.WriteUInt64(m_reqfile->GetFileSize());
228 }
229
230 AddDebugLogLineN(logClient, wxT("Sending file request to client"));
231
232 dataFileReq.WriteUInt8(OP_REQUESTFILENAME);
233 DEBUG_ONLY( sent_opcodes += wxT("|RFNM|"); )
234 // Extended information
235 if (GetExtendedRequestsVersion() > 0) {
236 m_reqfile->WritePartStatus(&dataFileReq);
237 }
238 if (GetExtendedRequestsVersion() > 1) {
239 m_reqfile->WriteCompleteSourcesCount(&dataFileReq);
240 }
241 if (m_reqfile->GetPartCount() > 1) {
242 DEBUG_ONLY( sent_opcodes += wxT("|RFID|"); )
243 dataFileReq.WriteUInt8(OP_SETREQFILEID);
244 }
245 if (IsEmuleClient()) {
246 SetRemoteQueueFull( true );
247 SetRemoteQueueRank(0);
248 }
249 if (IsSourceRequestAllowed()) {
250 if (SupportsSourceExchange2()){
251 DEBUG_ONLY( sent_opcodes += wxT("|RSRC2|"); )
252 dataFileReq.WriteUInt8(OP_REQUESTSOURCES2);
253 dataFileReq.WriteUInt8(SOURCEEXCHANGE2_VERSION);
254 const uint16 nOptions = 0; // 16 ... Reserved
255 dataFileReq.WriteUInt16(nOptions);
256 } else {
257 DEBUG_ONLY( sent_opcodes += wxT("|RSRC|"); )
258 dataFileReq.WriteUInt8(OP_REQUESTSOURCES);
259 }
260 m_reqfile->SetLastAnsweredTimeTimeout();
261 SetLastAskedForSources();
262 }
263 if (IsSupportingAICH()) {
264 DEBUG_ONLY( sent_opcodes += wxT("|AFHR|"); )
265 dataFileReq.WriteUInt8(OP_AICHFILEHASHREQ);
266 }
267 CPacket* packet = new CPacket(dataFileReq, OP_EMULEPROT, (SupportExtMultiPacket() ? OP_MULTIPACKET_EXT : OP_MULTIPACKET));
268 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
269 AddDebugLogLineN(logLocalClient, CFormat(wxT("Local Client: %s (%s) to %s"))
270 % (SupportExtMultiPacket() ? wxT("OP_MULTIPACKET_EXT") : wxT("OP_MULTIPACKET")) % sent_opcodes % GetFullIP());
271 SendPacket(packet, true);
272 } else {
273 //This is extended information
274 if (GetExtendedRequestsVersion() > 0 ) {
275 m_reqfile->WritePartStatus(&dataFileReq);
276 }
277 if (GetExtendedRequestsVersion() > 1 ) {
278 m_reqfile->WriteCompleteSourcesCount(&dataFileReq);
279 }
280 CPacket* packet = new CPacket(dataFileReq, OP_EDONKEYPROT, OP_REQUESTFILENAME);
281 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
282 AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_REQUESTFILENAME to ") + GetFullIP() );
283 SendPacket(packet, true);
284
285 // 26-Jul-2003: removed requesting the file status for files <= PARTSIZE for better compatibility with ed2k protocol (eDonkeyHybrid).
286 // if the remote client answers the OP_REQUESTFILENAME with OP_REQFILENAMEANSWER the file is shared by the remote client. if we
287 // know that the file is shared, we know also that the file is complete and don't need to request the file status.
288
289 // Sending the packet could have deleted the client, check m_reqfile
290 if (m_reqfile && (m_reqfile->GetPartCount() > 1)) {
291 CMemFile dataSetReqFileID(16);
292 dataSetReqFileID.WriteHash(m_reqfile->GetFileHash());
293 packet = new CPacket(dataSetReqFileID, OP_EDONKEYPROT, OP_SETREQFILEID);
294 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
295 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_SETREQFILEID to ") + GetFullIP());
296 SendPacket(packet, true);
297 }
298
299 if (IsEmuleClient()) {
300 SetRemoteQueueFull( true );
301 SetRemoteQueueRank(0);
302 }
303
304 // Sending the packet could have deleted the client, check m_reqfile
305 if (m_reqfile && IsSourceRequestAllowed()) {
306 m_reqfile->SetLastAnsweredTimeTimeout();
307
308 CMemFile packetdata;
309
310 if (SupportsSourceExchange2()) {
311 packetdata.WriteUInt8(SOURCEEXCHANGE2_VERSION);
312 packetdata.WriteUInt16(0 /* Reserved */);
313 }
314
315 packetdata.WriteHash(m_reqfile->GetFileHash());
316
317 packet = new CPacket(packetdata, OP_EMULEPROT, SupportsSourceExchange2() ? OP_REQUESTSOURCES2 : OP_REQUESTSOURCES);
318
319 theStats::AddUpOverheadSourceExchange(packet->GetPacketSize());
320 AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_REQUESTSOURCES to ") + GetFullIP() );
321 SendPacket(packet,true,true);
322 SetLastAskedForSources();
323 }
324
325 // Sending the packet could have deleted the client, check m_reqfile
326 if (m_reqfile && IsSupportingAICH()) {
327 packet = new CPacket(OP_AICHFILEHASHREQ,16,OP_EMULEPROT);
328 packet->Copy16ToDataBuffer((const char *)m_reqfile->GetFileHash().GetHash());
329 theStats::AddUpOverheadOther(packet->GetPacketSize());
330 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_AICHFILEHASHREQ to ") + GetFullIP());
331 SendPacket(packet,true,true);
332 }
333 }
334 }
335
336
ProcessFileInfo(const CMemFile * data,const CPartFile * file)337 void CUpDownClient::ProcessFileInfo(const CMemFile* data, const CPartFile* file)
338 {
339 // 0.42e
340 if (file==NULL) {
341 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; file==NULL)"));
342 }
343 if (m_reqfile==NULL) {
344 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; m_reqfile==NULL)"));
345 }
346 if (file != m_reqfile) {
347 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileInfo; m_reqfile!=file)"));
348 }
349
350 m_clientFilename = data->ReadString((GetUnicodeSupport() != utf8strNone));
351
352 // 26-Jul-2003: removed requesting the file status for files <= PARTSIZE for better compatibility with ed2k protocol (eDonkeyHybrid).
353 // if the remote client answers the OP_REQUESTFILENAME with OP_REQFILENAMEANSWER the file is shared by the remote client. if we
354 // know that the file is shared, we know also that the file is complete and don't need to request the file status.
355 if (m_reqfile->GetPartCount() == 1) {
356 m_nPartCount = m_reqfile->GetPartCount();
357
358 m_reqfile->UpdatePartsFrequency( this, false ); // Decrement
359 m_downPartStatus.setsize( m_nPartCount, 1 );
360 m_reqfile->UpdatePartsFrequency( this, true ); // Increment
361
362 m_bCompleteSource = true;
363
364 UpdateDisplayedInfo();
365 // even if the file is <= PARTSIZE, we _may_ need the hashset for that file (if the file size == PARTSIZE)
366 if (m_reqfile->IsHashSetNeeded()) {
367 if (m_socket) {
368 CPacket* packet = new CPacket(OP_HASHSETREQUEST,16, OP_EDONKEYPROT);
369 packet->Copy16ToDataBuffer((const char *)m_reqfile->GetFileHash().GetHash());
370 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
371 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_HASHSETREQUEST to ") + GetFullIP());
372 SendPacket(packet,true,true);
373 SetDownloadState(DS_REQHASHSET);
374 m_fHashsetRequesting = 1;
375 m_reqfile->SetHashSetNeeded(false);
376 } else {
377 wxFAIL;
378 }
379 } else {
380 SendStartupLoadReq();
381 }
382 m_reqfile->UpdatePartsInfo();
383 }
384 }
385
ProcessFileStatus(bool bUdpPacket,const CMemFile * data,const CPartFile * file)386 void CUpDownClient::ProcessFileStatus(bool bUdpPacket, const CMemFile* data, const CPartFile* file)
387 {
388 // 0.42e
389 wxString strReqFileNull(wxT("ERROR: Wrong file ID (ProcessFileStatus; m_reqfile==NULL)"));
390
391 if ( !m_reqfile || file != m_reqfile ){
392 if (!m_reqfile) {
393 throw strReqFileNull;
394 }
395 throw wxString(wxT("ERROR: Wrong file ID (ProcessFileStatus; m_reqfile!=file)"));
396 }
397
398 uint16 nED2KPartCount = data->ReadUInt16();
399
400 m_reqfile->UpdatePartsFrequency( this, false ); // Decrement
401 m_downPartStatus.clear();
402
403 bool bPartsNeeded = false;
404 if (!nED2KPartCount)
405 {
406 m_nPartCount = m_reqfile->GetPartCount();
407 m_downPartStatus.setsize( m_nPartCount, 1);
408 bPartsNeeded = true;
409 m_bCompleteSource = true;
410 }
411 else
412 {
413 // Somehow this happened.
414 if (!m_reqfile) {
415 throw strReqFileNull;
416 }
417 if (m_reqfile->GetED2KPartCount() != nED2KPartCount)
418 {
419 wxString strError;
420 strError << wxT("ProcessFileStatus - wrong part number recv=") << nED2KPartCount <<
421 wxT(" expected=") << m_reqfile->GetED2KPartCount() << wxT(" ") <<
422 m_reqfile->GetFileHash().Encode();
423 m_nPartCount = 0;
424 throw strError;
425 }
426 m_nPartCount = m_reqfile->GetPartCount();
427
428 m_bCompleteSource = false;
429 m_downPartStatus.setsize( m_nPartCount, 0 );
430 uint16 done = 0;
431
432 try {
433 while (done != m_nPartCount) {
434 uint8 toread = data->ReadUInt8();
435
436 for ( uint8 i = 0;i < 8; i++ ) {
437 bool status = ((toread>>i)&1)? 1:0;
438 m_downPartStatus.set(done, status);
439
440 if (status) {
441 if (!m_reqfile->IsComplete(done)){
442 bPartsNeeded = true;
443 }
444 }
445 done++;
446 if (done == m_nPartCount) {
447 break;
448 }
449 }
450 }
451 } catch( ... ) {
452 // We want the counts to be updated, even if we fail to read everything
453 m_reqfile->UpdatePartsFrequency( this, true ); // Increment
454
455 throw;
456 }
457 }
458
459 m_reqfile->UpdatePartsFrequency( this, true ); // Increment
460
461 UpdateDisplayedInfo();
462
463 // NOTE: This function is invoked from TCP and UDP socket!
464 if (!bUdpPacket) {
465 if (!bPartsNeeded) {
466 SetDownloadState(DS_NONEEDEDPARTS);
467 } else if (m_reqfile->IsHashSetNeeded()) {
468 //If we are using the eMule filerequest packets, this is taken care of in the Multipacket!
469 if (m_socket) {
470 CPacket* packet = new CPacket(OP_HASHSETREQUEST,16, OP_EDONKEYPROT);
471 packet->Copy16ToDataBuffer((const char *)m_reqfile->GetFileHash().GetHash());
472 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
473 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_HASHSETREQUEST to ") + GetFullIP());
474 SendPacket(packet, true, true);
475 SetDownloadState(DS_REQHASHSET);
476 m_fHashsetRequesting = 1;
477 m_reqfile->SetHashSetNeeded(false);
478 } else {
479 wxFAIL;
480 }
481 }
482 else {
483 SendStartupLoadReq();
484 }
485 }
486 else {
487 if (!bPartsNeeded) {
488 SetDownloadState(DS_NONEEDEDPARTS);
489 } else {
490 SetDownloadState(DS_ONQUEUE);
491 }
492 }
493 m_reqfile->UpdatePartsInfo();
494 }
495
AddRequestForAnotherFile(CPartFile * file)496 bool CUpDownClient::AddRequestForAnotherFile(CPartFile* file)
497 {
498 if ( m_A4AF_list.find( file ) == m_A4AF_list.end() ) {
499 // When we access a non-existing entry entry, it will be zeroed by default,
500 // so we have to set NeededParts. All in one go.
501 m_A4AF_list[file].NeededParts = true;
502 file->AddA4AFSource( this );
503 return true;
504 } else {
505 return false;
506 }
507 }
508
DeleteFileRequest(CPartFile * file)509 bool CUpDownClient::DeleteFileRequest(CPartFile* file)
510 {
511 return (m_A4AF_list.erase( file ) > 0);
512 }
513
DeleteAllFileRequests()514 void CUpDownClient::DeleteAllFileRequests()
515 {
516 m_A4AF_list.clear();
517 }
518
519
520 /* eMule 0.30c implementation, i give it a try (Creteil) BEGIN ... */
SetDownloadState(uint8 byNewState)521 void CUpDownClient::SetDownloadState(uint8 byNewState)
522 {
523 if (m_nDownloadState != byNewState) {
524 if (m_reqfile) {
525 // Notify the client that this source has changed its state
526 m_reqfile->ClientStateChanged( m_nDownloadState, byNewState );
527
528 if (byNewState == DS_DOWNLOADING) {
529 m_reqfile->AddDownloadingSource(this);
530 } else if (m_nDownloadState == DS_DOWNLOADING) {
531 m_reqfile->RemoveDownloadingSource(this);
532 }
533 }
534 if (byNewState == DS_DOWNLOADING) {
535 msReceivedPrev = GetTickCount();
536 theStats::AddDownloadingSource();
537 } else if (m_nDownloadState == DS_DOWNLOADING) {
538 theStats::RemoveDownloadingSource();
539 }
540
541 if (m_nDownloadState == DS_DOWNLOADING) {
542 m_nDownloadState = byNewState;
543 ClearDownloadBlockRequests();
544
545 kBpsDown = 0.0;
546 bytesReceivedCycle = 0;
547 msReceivedPrev = 0;
548 if (byNewState == DS_NONE) {
549 if (m_reqfile) {
550 m_reqfile->UpdatePartsFrequency( this, false ); // Decrement
551 }
552 m_downPartStatus.clear();
553 m_nPartCount = 0;
554 }
555 if (m_socket && byNewState != DS_ERROR) {
556 m_socket->DisableDownloadLimit();
557 }
558 }
559 m_nDownloadState = byNewState;
560 if(GetDownloadState() == DS_DOWNLOADING) {
561 if (IsEmuleClient()) {
562 SetRemoteQueueFull(false);
563 }
564 SetRemoteQueueRank(0); // eMule 0.30c set like this ...
565 }
566 UpdateDisplayedInfo(true);
567 }
568 }
569 /* eMule 0.30c implementation, i give it a try (Creteil) END ... */
570
ProcessHashSet(const uint8_t * packet,uint32 size)571 void CUpDownClient::ProcessHashSet(const uint8_t* packet, uint32 size)
572 {
573 if ((!m_reqfile) || md4cmp(packet,m_reqfile->GetFileHash().GetHash())) {
574 throw wxString(wxT("Wrong fileid sent (ProcessHashSet)"));
575 }
576 if (!m_fHashsetRequesting) {
577 throw wxString(wxT("Received unsolicited hashset, ignoring it."));
578 }
579 CMemFile data(packet,size);
580 if (m_reqfile->LoadHashsetFromFile(&data,true)) {
581 m_fHashsetRequesting = 0;
582 } else {
583 m_reqfile->SetHashSetNeeded(true);
584 throw wxString(wxT("Corrupted or invalid hashset received"));
585 }
586 SendStartupLoadReq();
587 }
588
SendBlockRequests()589 void CUpDownClient::SendBlockRequests()
590 {
591 uint32 current_time = ::GetTickCount();
592 if (GetVBTTags()) {
593
594 // Ask new blocks only when all completed
595 if (!m_PendingBlocks_list.empty()) {
596 return;
597 }
598
599 if ((m_dwLastBlockReceived + SEC2MS(5)) > current_time) {
600 // We received last block in less than 5 secs? Let's request faster.
601 m_MaxBlockRequests = m_MaxBlockRequests << 1;
602 if ( m_MaxBlockRequests > 0x20) {
603 m_MaxBlockRequests = 0x20;
604 }
605 } else {
606 m_MaxBlockRequests = m_MaxBlockRequests >> 1;
607 if ( m_MaxBlockRequests < STANDARD_BLOCKS_REQUEST) {
608 m_MaxBlockRequests = STANDARD_BLOCKS_REQUEST;
609 }
610 }
611 }
612
613 m_dwLastBlockReceived = current_time;
614
615 if (!m_reqfile) {
616 return;
617 }
618
619 if (m_DownloadBlocks_list.empty()) {
620 // Barry - instead of getting 3, just get how many is needed
621 uint16 count = m_MaxBlockRequests - m_PendingBlocks_list.size();
622 std::vector<Requested_Block_Struct*> toadd;
623 if (m_reqfile->GetNextRequestedBlock(this, toadd, count)) {
624 for (int i = 0; i != count; i++) {
625 m_DownloadBlocks_list.push_back(toadd[i]);
626 }
627 }
628 }
629
630 // Barry - Why are unfinished blocks requested again, not just new ones?
631
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();
641 }
642
643
644 if (m_PendingBlocks_list.empty()) {
645
646 CUpDownClient* slower_client = NULL;
647
648 if (thePrefs::GetDropSlowSources()) {
649 slower_client = m_reqfile->GetSlowerDownloadingClient(m_lastaverage, this);
650 }
651
652 if (slower_client == NULL) {
653 slower_client = this;
654 }
655
656 if (!slower_client->GetSentCancelTransfer()) {
657 CPacket* packet = new CPacket(OP_CANCELTRANSFER, 0, OP_EDONKEYPROT);
658 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
659 // if (slower_client != this) {
660 // printf("Dropped client %p to allow client %p to download\n",slower_client, this);
661 // }
662 slower_client->ClearDownloadBlockRequests();
663 slower_client->SendPacket(packet,true,true);
664 slower_client->SetSentCancelTransfer(1);
665 }
666
667 slower_client->SetDownloadState(DS_NONEEDEDPARTS);
668
669 if (slower_client != this) {
670 // Re-request freed blocks.
671 AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_CANCELTRANSFER (faster source eager to transfer) to ") + slower_client->GetFullIP() );
672 wxASSERT(m_DownloadBlocks_list.empty());
673 wxASSERT(m_PendingBlocks_list.empty());
674 uint16 count = m_MaxBlockRequests;
675 std::vector<Requested_Block_Struct*> toadd;
676 if (m_reqfile->GetNextRequestedBlock(this, toadd, count)) {
677 for (int i = 0; i != count; i++) {
678 Pending_Block_Struct* pblock = new Pending_Block_Struct;
679 pblock->block = toadd[i];
680 pblock->zStream = NULL;
681 pblock->totalUnzipped = 0;
682 pblock->fZStreamError = 0;
683 pblock->fRecovered = 0;
684 m_PendingBlocks_list.push_back(pblock);
685 }
686 } else {
687 // WTF, we just freed blocks.
688 wxFAIL_MSG(wxT("No free blocks to request after freeing some blocks"));
689 return;
690 }
691 } else {
692 // Drop this one.
693 AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_CANCELTRANSFER (no free blocks) to ") + GetFullIP() );
694 //#warning Kry - Would be nice to swap A4AF here.
695 return;
696 }
697 }
698
699 CPacket* packet = NULL;
700
701 if (GetVBTTags()) {
702 // ED2Kv2 packet...
703 // Most common scenario: hash + blocks to request + every one
704 // having 2 uint32 tags
705
706 uint8 nBlocks = m_PendingBlocks_list.size();
707 if (nBlocks > m_MaxBlockRequests) {
708 nBlocks = m_MaxBlockRequests;
709 }
710
711 CMemFile data(16 + 1 + nBlocks*((2+4)*2));
712
713 data.WriteHash(m_reqfile->GetFileHash());
714
715 data.WriteUInt8(nBlocks);
716
717 std::list<Pending_Block_Struct*>::iterator it = m_PendingBlocks_list.begin();
718 while (nBlocks) {
719 wxASSERT(it != m_PendingBlocks_list.end());
720 wxASSERT( (*it)->block->StartOffset <= (*it)->block->EndOffset );
721 (*it)->fZStreamError = 0;
722 (*it)->fRecovered = 0;
723 CTagVarInt(/*Noname*/0,(*it)->block->StartOffset).WriteTagToFile(&data);
724 CTagVarInt(/*Noname*/0,(*it)->block->EndOffset).WriteTagToFile(&data);
725 ++it;
726 nBlocks--;
727 }
728
729 packet = new CPacket(data, OP_ED2KV2HEADER, OP_REQUESTPARTS);
730 AddDebugLogLineN( logLocalClient, CFormat(wxT("Local Client ED2Kv2: OP_REQUESTPARTS(%i) to %s"))
731 % (m_PendingBlocks_list.size()<m_MaxBlockRequests ? m_PendingBlocks_list.size() : m_MaxBlockRequests) % GetFullIP() );
732
733 } else {
734 wxASSERT(m_MaxBlockRequests == STANDARD_BLOCKS_REQUEST);
735
736 //#warning Kry - I dont specially like this approach, we iterate one time too many
737
738 bool bHasLongBlocks = false;
739
740 std::list<Pending_Block_Struct*>::iterator it = m_PendingBlocks_list.begin();
741 for (uint32 i = 0; i != m_MaxBlockRequests; i++){
742 if (it != m_PendingBlocks_list.end()) {
743 Pending_Block_Struct* pending = *it++;
744 wxASSERT( pending->block->StartOffset <= pending->block->EndOffset );
745 if (pending->block->StartOffset > 0xFFFFFFFF || pending->block->EndOffset > 0xFFFFFFFF){
746 bHasLongBlocks = true;
747 if (!SupportsLargeFiles()){
748 // Requesting a large block from a client that doesn't support large files?
749 if (!GetSentCancelTransfer()){
750 CPacket* cancel_packet = new CPacket(OP_CANCELTRANSFER, 0, OP_EDONKEYPROT);
751 theStats::AddUpOverheadFileRequest(cancel_packet->GetPacketSize());
752 AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
753 SendPacket(cancel_packet,true,true);
754 SetSentCancelTransfer(1);
755 }
756 SetDownloadState(DS_ERROR);
757 return;
758 }
759 break;
760 }
761 }
762 }
763
764 CMemFile data(16 /*Hash*/ + (m_MaxBlockRequests*(bHasLongBlocks ? 8 : 4) /* uint32/64 start*/) + (3*(bHasLongBlocks ? 8 : 4)/* uint32/64 end*/));
765 data.WriteHash(m_reqfile->GetFileHash());
766
767 it = m_PendingBlocks_list.begin();
768 for (uint32 i = 0; i != m_MaxBlockRequests; i++) {
769 if (it != m_PendingBlocks_list.end()) {
770 Pending_Block_Struct* pending = *it++;
771 wxASSERT( pending->block->StartOffset <= pending->block->EndOffset );
772 pending->fZStreamError = 0;
773 pending->fRecovered = 0;
774 if (bHasLongBlocks) {
775 data.WriteUInt64(pending->block->StartOffset);
776 } else {
777 data.WriteUInt32(pending->block->StartOffset);
778 }
779 } else {
780 if (bHasLongBlocks) {
781 data.WriteUInt64(0);
782 } else {
783 data.WriteUInt32(0);
784 }
785 }
786 }
787
788 it = m_PendingBlocks_list.begin();
789 for (uint32 i = 0; i != m_MaxBlockRequests; i++) {
790 if (it != m_PendingBlocks_list.end()) {
791 Requested_Block_Struct* block = (*it++)->block;
792 if (bHasLongBlocks) {
793 data.WriteUInt64(block->EndOffset+1);
794 } else {
795 data.WriteUInt32(block->EndOffset+1);
796 }
797 } else {
798 if (bHasLongBlocks) {
799 data.WriteUInt64(0);
800 } else {
801 data.WriteUInt32(0);
802 }
803 }
804 }
805 packet = new CPacket(data, (bHasLongBlocks ? OP_EMULEPROT : OP_EDONKEYPROT), (bHasLongBlocks ? (uint8)OP_REQUESTPARTS_I64 : (uint8)OP_REQUESTPARTS));
806 AddDebugLogLineN(logLocalClient, CFormat(wxT("Local Client: %s to %s")) % (bHasLongBlocks ? wxT("OP_REQUESTPARTS_I64") : wxT("OP_REQUESTPARTS")) % GetFullIP());
807 }
808
809 if (packet) {
810 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
811 SendPacket(packet, true, true);
812 }
813 }
814
815 /*
816 Barry - Originally this only wrote to disk when a full 180k block
817 had been received from a client, and only asked for data in
818 180k blocks.
819
820 This meant that on average 90k was lost for every connection
821 to a client data source. That is a lot of wasted data.
822
823 To reduce the lost data, packets are now written to a buffer
824 and flushed to disk regularly regardless of size downloaded.
825
826 This includes compressed packets.
827
828 Data is also requested only where gaps are, not in 180k blocks.
829 The requests will still not exceed 180k, but may be smaller to
830 fill a gap.
831 */
832
ProcessBlockPacket(const uint8_t * packet,uint32 size,bool packed,bool largeblocks)833 void CUpDownClient::ProcessBlockPacket(const uint8_t* packet, uint32 size, bool packed, bool largeblocks)
834 {
835 // Ignore if no data required
836 if (!(GetDownloadState() == DS_DOWNLOADING || GetDownloadState() == DS_NONEEDEDPARTS)) {
837 return;
838 }
839
840 // This vars are defined here to be able to use them on the catch
841 int header_size = 16;
842 uint64 nStartPos = 0;
843 uint64 nEndPos = 0;
844 uint32 nBlockSize = 0;
845 uint32 lenUnzipped = 0;
846
847 // Update stats
848 m_dwLastBlockReceived = ::GetTickCount();
849
850 try {
851
852 // Read data from packet
853 const CMemFile data(packet, size);
854
855 // Check that this data is for the correct file
856 if ((!m_reqfile) || data.ReadHash() != m_reqfile->GetFileHash()) {
857 throw wxString(wxT("Wrong fileid sent (ProcessBlockPacket)"));
858 }
859
860 // Find the start & end positions, and size of this chunk of data
861
862 if (largeblocks) {
863 nStartPos = data.ReadUInt64();
864 header_size += 8;
865 } else {
866 nStartPos = data.ReadUInt32();
867 header_size += 4;
868 }
869
870 if (packed) {
871 nBlockSize = data.ReadUInt32();
872 header_size += 4;
873 nEndPos = nStartPos + (size - header_size);
874 } else {
875 if (largeblocks) {
876 nEndPos = data.ReadUInt64();
877 header_size += 8;
878 } else {
879 nEndPos = data.ReadUInt32();
880 header_size += 4;
881 }
882 }
883
884 // Check that packet size matches the declared data size + header size
885 if ( nEndPos == nStartPos || size != ((nEndPos - nStartPos) + header_size)) {
886 throw wxString(wxT("Corrupted or invalid DataBlock received (ProcessBlockPacket)"));
887 }
888 theStats::AddDownloadFromSoft(GetClientSoft(),size - header_size);
889 bytesReceivedCycle += size - header_size;
890
891 credits->AddDownloaded(size - header_size, GetIP(), theApp->CryptoAvailable());
892
893 // Move end back one, should be inclusive
894 nEndPos--;
895
896 // Loop through to find the reserved block that this is within
897 std::list<Pending_Block_Struct*>::iterator it = m_PendingBlocks_list.begin();
898 for (; it != m_PendingBlocks_list.end(); ++it) {
899 Pending_Block_Struct* cur_block = *it;
900
901 if ((cur_block->block->StartOffset <= nStartPos) && (cur_block->block->EndOffset >= nStartPos)) {
902 // Found reserved block
903
904 if (cur_block->block->StartOffset == nStartPos) {
905 // This block just started transfering. Set the start time.
906 m_last_block_start = ::GetTickCountFullRes();
907 }
908
909 if (cur_block->fZStreamError){
910 AddDebugLogLineN(logZLib,
911 CFormat(wxT("Ignoring %u bytes of block %u-%u because of erroneous zstream state for file: %s"))
912 % (size - header_size) % nStartPos % nEndPos % m_reqfile->GetFileName());
913 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
914 return;
915 }
916
917 // Remember this start pos, used to draw part downloading in list
918 m_lastDownloadingPart = nStartPos / PARTSIZE;
919
920 // Occasionally packets are duplicated, no point writing it twice
921 // This will be 0 in these cases, or the length written otherwise
922 uint32 lenWritten = 0;
923
924 // Handle differently depending on whether packed or not
925 if (!packed) {
926 // security sanitize check
927 if (nEndPos > cur_block->block->EndOffset) {
928 AddDebugLogLineN(logRemoteClient, CFormat(wxT("Received Blockpacket exceeds requested boundaries (requested end: %u, Part: %u, received end: %u, Part: %u), file: %s remote IP: %s")) % cur_block->block->EndOffset % (uint32)(cur_block->block->EndOffset / PARTSIZE) % nEndPos % (uint32)(nEndPos / PARTSIZE) % m_reqfile->GetFileName() % Uint32toStringIP(GetIP()));
929 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
930 return;
931 }
932 // Write to disk (will be buffered in part file class)
933 lenWritten = m_reqfile->WriteToBuffer( size - header_size, (uint8_t*)(packet + header_size), nStartPos, nEndPos, cur_block->block, this);
934 } else {
935 // Packed
936 wxASSERT( (long int)size > 0 );
937 // Create space to store unzipped data, the size is
938 // only an initial guess, will be resized in unzip()
939 // if not big enough
940 lenUnzipped = (size * 2);
941 // Don't get too big
942 if (lenUnzipped > (BLOCKSIZE + 300)) {
943 lenUnzipped = (BLOCKSIZE + 300);
944 }
945 uint8_t *unzipped = new uint8_t[lenUnzipped];
946
947 // Try to unzip the packet
948 int result = unzip(cur_block, (uint8_t*)(packet + header_size), (size - header_size), &unzipped, &lenUnzipped);
949
950 // no block can be uncompressed to >2GB, 'lenUnzipped' is obviously erroneous.
951 if (result == Z_OK && ((int)lenUnzipped >= 0)) {
952
953 // Write any unzipped data to disk
954 if (lenUnzipped > 0) {
955 wxASSERT( (int)lenUnzipped > 0 );
956
957 // Use the current start and end positions for the uncompressed data
958 nStartPos = cur_block->block->StartOffset + cur_block->totalUnzipped - lenUnzipped;
959 nEndPos = cur_block->block->StartOffset + cur_block->totalUnzipped - 1;
960
961 if (nStartPos > cur_block->block->EndOffset || nEndPos > cur_block->block->EndOffset) {
962 AddDebugLogLineN(logZLib,
963 CFormat(wxT("Corrupted compressed packet for '%s' received (error 666)")) % m_reqfile->GetFileName());
964 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
965 } else {
966 // Write uncompressed data to file
967 lenWritten = m_reqfile->WriteToBuffer( size - header_size,
968 unzipped,
969 nStartPos,
970 nEndPos,
971 cur_block->block,
972 this);
973 }
974 }
975 } else {
976 wxString strZipError;
977 if (cur_block->zStream && cur_block->zStream->msg) {
978 strZipError = wxT(" - ") + wxString::FromAscii(cur_block->zStream->msg);
979 }
980
981 AddDebugLogLineN(logZLib,
982 CFormat(wxT("Corrupted compressed packet for '%s' received (error %i): %s"))
983 % m_reqfile->GetFileName() % result % strZipError);
984
985 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
986
987 // If we had an zstream error, there is no chance that we could recover from it nor that we
988 // could use the current zstream (which is in error state) any longer.
989 if (cur_block->zStream){
990 inflateEnd(cur_block->zStream);
991 delete cur_block->zStream;
992 cur_block->zStream = NULL;
993 }
994
995 // Although we can't further use the current zstream, there is no need to disconnect the sending
996 // client because the next zstream (a series of 10K-blocks which build a 180K-block) could be
997 // valid again. Just ignore all further blocks for the current zstream.
998 cur_block->fZStreamError = 1;
999 cur_block->totalUnzipped = 0; // bluecow's fix
1000 }
1001 delete [] unzipped;
1002 }
1003 // These checks only need to be done if any data was written
1004 if (lenWritten > 0) {
1005 m_nTransferredDown += lenWritten;
1006
1007 // If finished reserved block
1008 if (nEndPos == cur_block->block->EndOffset) {
1009
1010 // Save last average speed based on data and time.
1011 // This should do bytes/sec.
1012 uint32 average_time = (::GetTickCountFullRes() - m_last_block_start);
1013
1014 // Avoid divide by 0.
1015 if (average_time == 0) {
1016 average_time++;
1017 }
1018
1019 m_lastaverage = ((cur_block->block->EndOffset - cur_block->block->StartOffset) * 1000) / average_time;
1020
1021 m_reqfile->RemoveBlockFromList(cur_block->block->StartOffset, cur_block->block->EndOffset);
1022 delete cur_block->block;
1023 // Not always allocated
1024 if (cur_block->zStream) {
1025 inflateEnd(cur_block->zStream);
1026 delete cur_block->zStream;
1027 }
1028 delete cur_block;
1029 m_PendingBlocks_list.erase(it);
1030
1031 // Request next block
1032 SendBlockRequests();
1033 }
1034 }
1035 // Stop looping and exit method
1036 return;
1037 }
1038 }
1039 } catch (const CEOFException& e) {
1040 wxString error = wxString(wxT("Error reading "));
1041 if (packed) error += CFormat(wxT("packed (LU: %i) largeblocks ")) % lenUnzipped;
1042 error += CFormat(wxT("data packet: RS: %i HS: %i SP: %i EP: %i BS: %i -> "))
1043 % size % header_size % nStartPos % nEndPos % nBlockSize;
1044 AddDebugLogLineC(logRemoteClient, error + e.what());
1045 return;
1046 }
1047 }
1048
unzip(Pending_Block_Struct * block,uint8_t * zipped,uint32 lenZipped,uint8_t ** unzipped,uint32 * lenUnzipped,int iRecursion)1049 int CUpDownClient::unzip(Pending_Block_Struct *block, uint8_t *zipped, uint32 lenZipped, uint8_t **unzipped, uint32 *lenUnzipped, int iRecursion)
1050 {
1051 int err = Z_DATA_ERROR;
1052
1053 // Save some typing
1054 z_stream *zS = block->zStream;
1055
1056 // Is this the first time this block has been unzipped
1057 if (zS == NULL) {
1058 // Create stream
1059 block->zStream = new z_stream;
1060 zS = block->zStream;
1061
1062 // Initialise stream values
1063 zS->zalloc = (alloc_func)0;
1064 zS->zfree = (free_func)0;
1065 zS->opaque = (voidpf)0;
1066
1067 // Set output data streams, do this here to avoid overwriting on recursive calls
1068 zS->next_out = (*unzipped);
1069 zS->avail_out = (*lenUnzipped);
1070
1071 // Initialise the z_stream
1072 err = inflateInit(zS);
1073 if (err != Z_OK) {
1074 return err;
1075 }
1076 }
1077
1078 // Use whatever input is provided
1079 zS->next_in = zipped;
1080 zS->avail_in = lenZipped;
1081
1082 // Only set the output if not being called recursively
1083 if (iRecursion == 0) {
1084 zS->next_out = (*unzipped);
1085 zS->avail_out = (*lenUnzipped);
1086 }
1087
1088 // Try to unzip the data
1089 err = inflate(zS, Z_SYNC_FLUSH);
1090
1091 // Is zip finished reading all currently available input and writing
1092 // all generated output
1093 if (err == Z_STREAM_END) {
1094 // Finish up
1095 err = inflateEnd(zS);
1096 if (err != Z_OK) {
1097 return err;
1098 }
1099
1100 // Got a good result, set the size to the amount unzipped in this call
1101 // (including all recursive calls)
1102 (*lenUnzipped) = (zS->total_out - block->totalUnzipped);
1103 block->totalUnzipped = zS->total_out;
1104 } else if ((err == Z_OK) && (zS->avail_out == 0) && (zS->avail_in != 0)) {
1105
1106 // Output array was not big enough,
1107 // call recursively until there is enough space
1108
1109 // What size should we try next
1110 uint32 newLength = (*lenUnzipped) *= 2;
1111 if (newLength == 0) {
1112 newLength = lenZipped * 2;
1113 }
1114 // Copy any data that was successfully unzipped to new array
1115 uint8_t *temp = new uint8_t[newLength];
1116 wxASSERT( zS->total_out - block->totalUnzipped <= newLength );
1117 memcpy(temp, (*unzipped), (zS->total_out - block->totalUnzipped));
1118 delete [] (*unzipped);
1119 (*unzipped) = temp;
1120 (*lenUnzipped) = newLength;
1121
1122 // Position stream output to correct place in new array
1123 zS->next_out = (*unzipped) + (zS->total_out - block->totalUnzipped);
1124 zS->avail_out = (*lenUnzipped) - (zS->total_out - block->totalUnzipped);
1125
1126 // Try again
1127 err = unzip(block, zS->next_in, zS->avail_in, unzipped, lenUnzipped, iRecursion + 1);
1128 } else if ((err == Z_OK) && (zS->avail_in == 0)) {
1129 // All available input has been processed, everything ok.
1130 // Set the size to the amount unzipped in this call
1131 // (including all recursive calls)
1132 (*lenUnzipped) = (zS->total_out - block->totalUnzipped);
1133 block->totalUnzipped = zS->total_out;
1134 } else {
1135 // Should not get here unless input data is corrupt
1136 wxString strZipError;
1137
1138 if ( zS->msg ) {
1139 strZipError = CFormat(wxT(" %d '%s'")) % err % wxString::FromAscii(zS->msg);
1140 } else if (err != Z_OK) {
1141 strZipError = CFormat(wxT(" %d")) % err;
1142 }
1143
1144 AddDebugLogLineN(logZLib,
1145 CFormat(wxT("Unexpected zip error %s in file '%s'"))
1146 % strZipError % (m_reqfile ? m_reqfile->GetFileName() : CPath(wxT("?"))));
1147 }
1148
1149 if (err != Z_OK) {
1150 (*lenUnzipped) = 0;
1151 }
1152
1153 return err;
1154 }
1155
1156
1157 // Speed is now updated only when data was received, calculated as
1158 // (data received) / (time since last receiption)
1159 // and slightly filtered (10s average).
1160 // Result is quite precise now and makes the DownloadRateAdjust workaround obsolete.
1161
CalculateKBpsDown()1162 float CUpDownClient::CalculateKBpsDown()
1163 {
1164 const float tAverage = 10.0;
1165 uint32 msCur = GetTickCount();
1166
1167 if (bytesReceivedCycle) {
1168 float dt = (msCur - msReceivedPrev) / 1000.0; // time since last reception
1169 if (dt < 0.01) { // (safeguard against divide-by-zero)
1170 dt = 0.01f; // diff should be 100ms actually
1171 }
1172 float kBpsDownCur = bytesReceivedCycle / 1024.0 / dt;
1173 if (dt >= tAverage) {
1174 kBpsDown = kBpsDownCur;
1175 } else {
1176 kBpsDown = (kBpsDown * (tAverage - dt) + kBpsDownCur * dt) / tAverage;
1177 }
1178 //AddDebugLogLineN(logLocalClient, CFormat(wxT("CalculateKBpsDown %p kbps %.1f kbpsCur %.1f dt %.3f rcv %d "))
1179 // % this % kBpsDown % kBpsDownCur % dt % bytesReceivedCycle);
1180 bytesReceivedCycle = 0;
1181 msReceivedPrev = msCur;
1182 }
1183
1184 m_cShowDR++;
1185 if (m_cShowDR == 30){
1186 m_cShowDR = 0;
1187 UpdateDisplayedInfo();
1188 }
1189 if (msCur - m_dwLastBlockReceived > DOWNLOADTIMEOUT) {
1190 if (!GetSentCancelTransfer()){
1191 CPacket* packet = new CPacket(OP_CANCELTRANSFER, 0, OP_EDONKEYPROT);
1192 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
1193 AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_CANCELTRANSFER to ") + GetFullIP() );
1194 SendPacket(packet,true,true);
1195 SetSentCancelTransfer(1);
1196 }
1197 SetDownloadState(DS_ONQUEUE);
1198 }
1199
1200 return kBpsDown;
1201 }
1202
GetAvailablePartCount() const1203 uint16 CUpDownClient::GetAvailablePartCount() const
1204 {
1205 uint16 result = 0;
1206 for (int i = 0;i != m_nPartCount;i++){
1207 if (IsPartAvailable(i))
1208 result++;
1209 }
1210 return result;
1211 }
1212
SetRemoteQueueRank(uint16 nr)1213 void CUpDownClient::SetRemoteQueueRank(uint16 nr)
1214 {
1215 m_nOldRemoteQueueRank = m_nRemoteQueueRank;
1216 m_nRemoteQueueRank = nr;
1217 UpdateDisplayedInfo();
1218 }
1219
UDPReaskACK(uint16 nNewQR)1220 void CUpDownClient::UDPReaskACK(uint16 nNewQR)
1221 {
1222 // 0.42e
1223 m_bUDPPending = false;
1224 SetRemoteQueueRank(nNewQR);
1225 m_dwLastAskedTime = ::GetTickCount();
1226 }
1227
UDPReaskFNF()1228 void CUpDownClient::UDPReaskFNF()
1229 {
1230 m_bUDPPending = false;
1231
1232 // avoid premature deletion of 'this' client
1233 if (GetDownloadState() != DS_DOWNLOADING){
1234 if (m_reqfile) {
1235 m_reqfile->AddDeadSource(this);
1236 }
1237
1238 theApp->downloadqueue->RemoveSource(this);
1239 if (!m_socket) {
1240 if (Disconnected(wxT("UDPReaskFNF m_socket=NULL"))) {
1241 Safe_Delete();
1242 }
1243 }
1244 } else {
1245 AddDebugLogLineN( logRemoteClient, wxT("UDP ANSWER FNF : ") + GetUserName() + wxT(" - did not remove client because of current download state") );
1246 }
1247 }
1248
UDPReaskForDownload()1249 void CUpDownClient::UDPReaskForDownload()
1250 {
1251
1252 wxASSERT(m_reqfile);
1253
1254 if(!m_reqfile || m_bUDPPending ) {
1255 return;
1256 }
1257
1258 //#warning We should implement the quality tests for udp reliability
1259 /*
1260 if( m_nTotalUDPPackets > 3 && ((float)(m_nFailedUDPPackets/m_nTotalUDPPackets) > .3)) {
1261 return;
1262 }
1263 */
1264
1265 if (thePrefs::GetEffectiveUDPPort() == 0) {
1266 return;
1267 }
1268
1269 if (m_nUDPPort != 0 && !theApp->IsFirewalled() && !IsConnected()) {
1270 //don't use udp to ask for sources
1271 if(IsSourceRequestAllowed()) {
1272 return;
1273 }
1274
1275 m_bUDPPending = true;
1276
1277 CMemFile data(128);
1278 data.WriteHash(m_reqfile->GetFileHash());
1279
1280 if (GetUDPVersion() > 3) {
1281 if (m_reqfile->IsPartFile()) {
1282 static_cast<CPartFile*>(m_reqfile)->WritePartStatus(&data);
1283 }
1284 else {
1285 data.WriteUInt16(0);
1286 }
1287 }
1288
1289 if (GetUDPVersion() > 2) {
1290 data.WriteUInt16(m_reqfile->m_nCompleteSourcesCount);
1291 }
1292
1293 CPacket* response = new CPacket(data, OP_EMULEPROT, OP_REASKFILEPING);
1294 AddDebugLogLineN( logClientUDP, wxT("Client UDP socket: send OP_REASKFILEPING") );
1295 theStats::AddUpOverheadFileRequest(response->GetPacketSize());
1296 theApp->clientudp->SendPacket(response,GetConnectIP(),GetUDPPort(), ShouldReceiveCryptUDPPackets(), GetUserHash().GetHash(), false, 0);
1297 } else if (HasLowID() && GetBuddyIP() && GetBuddyPort() && HasValidBuddyID()) {
1298
1299 m_bUDPPending = true;
1300
1301 CMemFile data(128);
1302
1303 data.WriteHash(CMD4Hash(GetBuddyID()));
1304 data.WriteHash(m_reqfile->GetFileHash());
1305
1306 if (GetUDPVersion() > 3) {
1307 if (m_reqfile->IsPartFile()) {
1308 static_cast<CPartFile*>(m_reqfile)->WritePartStatus(&data);
1309 } else {
1310 data.WriteUInt16(0);
1311 }
1312 }
1313
1314 if (GetUDPVersion() > 2) {
1315 data.WriteUInt16(m_reqfile->m_nCompleteSourcesCount);
1316 }
1317
1318 CPacket* response = new CPacket(data, OP_EMULEPROT, OP_REASKCALLBACKUDP);
1319 AddDebugLogLineN( logClientUDP, wxT("Client UDP socket: send OP_REASKCALLBACKUDP") );
1320 theStats::AddUpOverheadFileRequest(response->GetPacketSize());
1321 theApp->clientudp->SendPacket(response, GetBuddyIP(), GetBuddyPort(), false, NULL, true, 0 );
1322 }
1323 }
1324
1325
1326 // Get the next part that is requested
GetNextRequestedPart() const1327 uint16 CUpDownClient::GetNextRequestedPart() const
1328 {
1329 uint16 part = 0xffff;
1330
1331 std::list<Pending_Block_Struct*>::const_iterator it = m_PendingBlocks_list.begin();
1332 for (; it != m_PendingBlocks_list.end(); ++it) {
1333 part = (*it)->block->StartOffset / PARTSIZE;
1334 if (part != m_lastDownloadingPart) {
1335 break;
1336 }
1337 }
1338
1339 return part;
1340 }
1341
1342
UpdateDisplayedInfo(bool force)1343 void CUpDownClient::UpdateDisplayedInfo(bool force)
1344 {
1345 uint32 curTick = ::GetTickCount();
1346 if (force || curTick-m_lastRefreshedDLDisplay > MINWAIT_BEFORE_DLDISPLAY_WINDOWUPDATE) {
1347 // Check if we actually need to notify of changes
1348 bool update = m_reqfile && m_reqfile->ShowSources();
1349
1350 // Check A4AF files only if needed
1351 if ( !update ) {
1352 A4AFList::iterator it = m_A4AF_list.begin();
1353 for ( ; it != m_A4AF_list.end(); ++it ) {
1354 if ( it->first->ShowSources() ) {
1355 update = true;
1356 break;
1357 }
1358 }
1359 }
1360
1361 // And finnaly trigger an event if there's any reason
1362 if ( update ) {
1363 SourceItemType type;
1364 switch (GetDownloadState()) {
1365 case DS_DOWNLOADING:
1366 case DS_ONQUEUE:
1367 // We will send A4AF, which will be checked.
1368 type = A4AF_SOURCE;
1369 break;
1370 default:
1371 type = UNAVAILABLE_SOURCE;
1372 break;
1373 }
1374
1375 Notify_SourceCtrlUpdateSource(ECID(), type );
1376 }
1377
1378 // Shared files view
1379 if (m_uploadingfile && m_uploadingfile->ShowPeers()) {
1380 Notify_SharedCtrlRefreshClient(ECID(), AVAILABLE_SOURCE);
1381 }
1382
1383 m_lastRefreshedDLDisplay = curTick;
1384 }
1385 }
1386
GetObfuscationStatus() const1387 uint8 CUpDownClient::GetObfuscationStatus() const
1388 {
1389 uint8 ret = OBST_UNDEFINED;
1390 if (thePrefs::IsClientCryptLayerSupported()) {
1391 if (SupportsCryptLayer()) {
1392 if ((RequestsCryptLayer() || thePrefs::IsClientCryptLayerRequested()) && HasObfuscatedConnectionBeenEstablished()) {
1393 ret = OBST_ENABLED;
1394 } else {
1395 ret = OBST_SUPPORTED;
1396 }
1397 } else {
1398 ret = OBST_NOT_SUPPORTED;
1399 }
1400 } else {
1401 ret = OBST_DISABLED;
1402 }
1403 return ret;
1404 }
1405
1406 // IgnoreNoNeeded = will switch to files of which this source has no needed parts (if no better fiels found)
1407 // ignoreSuspensions = ignore timelimit for A4Af jumping
1408 // bRemoveCompletely = do not readd the file which the source is swapped from to the A4AF lists (needed if deleting or stopping a file)
1409 // toFile = Try to swap to this partfile only
1410
SwapToAnotherFile(bool bIgnoreNoNeeded,bool ignoreSuspensions,bool bRemoveCompletely,CPartFile * toFile)1411 bool CUpDownClient::SwapToAnotherFile(bool bIgnoreNoNeeded, bool ignoreSuspensions, bool bRemoveCompletely, CPartFile* toFile)
1412 {
1413 // Fail if m_reqfile is invalid
1414 if ( m_reqfile == NULL ) {
1415 return false;
1416 }
1417
1418 // It would be stupid to swap away a downloading source
1419 if (GetDownloadState() == DS_DOWNLOADING) {
1420 return false;
1421 }
1422
1423 // The iterator of the final target
1424 A4AFList::iterator target = m_A4AF_list.end();
1425
1426 // Do we want to swap to a specific file?
1427 if ( toFile != NULL ) {
1428 A4AFList::iterator it = m_A4AF_list.find( toFile );
1429 if ( it != m_A4AF_list.end() ) {
1430
1431 // We force ignoring of timestamps
1432 if ( IsValidSwapTarget( it, bIgnoreNoNeeded, true ) ) {
1433 // Set the target
1434 target = it;
1435 }
1436 }
1437 } else {
1438 // We want highest priority possible, but need to start with
1439 // a value less than any other priority
1440 char priority = -1;
1441
1442 A4AFList::iterator it = m_A4AF_list.begin();
1443 for ( ; it != m_A4AF_list.end(); ++it ) {
1444 if ( IsValidSwapTarget( it, bIgnoreNoNeeded, ignoreSuspensions ) ) {
1445 char cur_priority = it->first->GetDownPriority();
1446
1447 // We would prefer to get files with needed parts, thus rate them higher.
1448 // However, this really only matters if bIgnoreNoNeeded is true.
1449 if ( it->second.NeededParts )
1450 cur_priority += 10;
1451
1452 // Change target if the current file has a higher rate than the previous
1453 if ( cur_priority > priority ) {
1454 priority = cur_priority;
1455
1456 // Set the new target
1457 target = it;
1458
1459 // Break on the first High-priority file with needed parts
1460 if ( priority == PR_HIGH + 10 ) {
1461 break;
1462 }
1463 }
1464 }
1465 }
1466 }
1467
1468 // Try to swap if we found a valid target
1469 if ( target != m_A4AF_list.end() ) {
1470
1471 // Sanity check, if reqfile doesn't own the source, then something
1472 // is wrong and the swap cannot proceed.
1473 if ( m_reqfile->DelSource( this ) ) {
1474 CPartFile* SwapTo = target->first;
1475
1476 // remove this client from the A4AF list of our new m_reqfile
1477 if ( SwapTo->RemoveA4AFSource( this ) ) {
1478 Notify_SourceCtrlRemoveSource(ECID(), SwapTo);
1479 }
1480
1481 m_reqfile->RemoveDownloadingSource( this );
1482
1483 // Do we want to remove it completly? Say if the old file is getting deleted
1484 if ( !bRemoveCompletely ) {
1485 m_reqfile->AddA4AFSource( this );
1486
1487 // Set the status of the old file
1488 m_A4AF_list[m_reqfile].NeededParts = (GetDownloadState() != DS_NONEEDEDPARTS);
1489
1490 // Avoid swapping to this file for a while
1491 m_A4AF_list[m_reqfile].timestamp = ::GetTickCount();
1492
1493 Notify_SourceCtrlAddSource(m_reqfile, CCLIENTREF(this, wxT("CUpDownClient::SwapToAnotherFile Notify_SourceCtrlAddSource 1")), A4AF_SOURCE);
1494 } else {
1495 Notify_SourceCtrlRemoveSource(ECID(), m_reqfile);
1496 }
1497
1498 SetDownloadState(DS_NONE);
1499 ResetFileStatusInfo();
1500
1501 m_nRemoteQueueRank = 0;
1502 m_nOldRemoteQueueRank = 0;
1503
1504 m_reqfile->UpdatePartsInfo();
1505
1506 SetRequestFile( SwapTo );
1507
1508 SwapTo->AddSource( this );
1509
1510 Notify_SourceCtrlAddSource(SwapTo, CCLIENTREF(this, wxT("CUpDownClient::SwapToAnotherFile Notify_SourceCtrlAddSource 2")), UNAVAILABLE_SOURCE);
1511
1512 // Remove the new reqfile from the list of other files
1513 m_A4AF_list.erase( target );
1514
1515 return true;
1516 }
1517 }
1518
1519 return false;
1520 }
1521
1522
IsValidSwapTarget(A4AFList::iterator it,bool ignorenoneeded,bool ignoresuspended)1523 bool CUpDownClient::IsValidSwapTarget( A4AFList::iterator it, bool ignorenoneeded, bool ignoresuspended )
1524 {
1525 wxASSERT( it != m_A4AF_list.end() && it->first );
1526
1527 // Check if this file has been suspended
1528 if ( !ignoresuspended ) {
1529 if ( ::GetTickCount() - it->second.timestamp >= PURGESOURCESWAPSTOP ) {
1530 // The wait-time has been exceeded and the file is now a valid target
1531 it->second.timestamp = 0;
1532 } else {
1533 // The file was still suspended and we are not ignoring suspensions
1534 return false;
1535 }
1536 }
1537
1538 // Check if the client has needed parts
1539 if ( !ignorenoneeded ) {
1540 if ( !it->second.NeededParts ) {
1541 return false;
1542 }
1543 }
1544
1545 // Final checks to see if the client is a valid target
1546 CPartFile* cur_file = it->first;
1547 if ( ( cur_file != m_reqfile && !cur_file->IsStopped() ) &&
1548 ( cur_file->GetStatus() == PS_READY || cur_file->GetStatus() == PS_EMPTY ) &&
1549 ( cur_file->IsPartFile() ) )
1550 {
1551 return true;
1552 } else {
1553 return false;
1554 }
1555 }
1556
1557
SetRequestFile(CPartFile * reqfile)1558 void CUpDownClient::SetRequestFile(CPartFile* reqfile)
1559 {
1560 if ( m_reqfile != reqfile ) {
1561 // Decrement the source-count of the old request-file
1562 if ( m_reqfile ) {
1563 m_reqfile->ClientStateChanged( GetDownloadState(), -1 );
1564 m_reqfile->UpdatePartsFrequency( this, false );
1565 }
1566
1567 m_nPartCount = 0;
1568 m_downPartStatus.clear();
1569
1570 m_reqfile = reqfile;
1571
1572 if ( reqfile ) {
1573 // Increment the source-count of the new request-file
1574 m_reqfile->ClientStateChanged( -1, GetDownloadState() );
1575
1576 m_nPartCount = reqfile->GetPartCount();
1577 }
1578 }
1579 }
1580
SetReqFileAICHHash(CAICHHash * val)1581 void CUpDownClient::SetReqFileAICHHash(CAICHHash* val){
1582 if(m_pReqFileAICHHash != NULL && m_pReqFileAICHHash != val)
1583 delete m_pReqFileAICHHash;
1584 m_pReqFileAICHHash = val;
1585 }
1586
SendAICHRequest(CPartFile * pForFile,uint16 nPart)1587 void CUpDownClient::SendAICHRequest(CPartFile* pForFile, uint16 nPart){
1588 CAICHRequestedData request;
1589 request.m_nPart = nPart;
1590 request.m_pClient.Link(this CLIENT_DEBUGSTRING("CUpDownClient::SendAICHRequest"));
1591 request.m_pPartFile = pForFile;
1592 CAICHHashSet::m_liRequestedData.push_back(request);
1593 m_fAICHRequested = TRUE;
1594 CMemFile data;
1595 data.WriteHash(pForFile->GetFileHash());
1596 data.WriteUInt16(nPart);
1597 pForFile->GetAICHHashset()->GetMasterHash().Write(&data);
1598 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_AICHREQUEST);
1599 theStats::AddUpOverheadOther(packet->GetPacketSize());
1600 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_AICHREQUEST to") + GetFullIP());
1601 SafeSendPacket(packet);
1602 }
1603
ProcessAICHAnswer(const uint8_t * packet,uint32 size)1604 void CUpDownClient::ProcessAICHAnswer(const uint8_t* packet, uint32 size)
1605 {
1606 if (m_fAICHRequested == FALSE){
1607 throw wxString(wxT("Received unrequested AICH Packet"));
1608 }
1609 m_fAICHRequested = FALSE;
1610
1611 CMemFile data(packet, size);
1612 if (size <= 16){
1613 CAICHHashSet::ClientAICHRequestFailed(this);
1614 return;
1615 }
1616
1617 CMD4Hash hash = data.ReadHash();
1618 CPartFile* pPartFile = theApp->downloadqueue->GetFileByID(hash);
1619 CAICHRequestedData request = CAICHHashSet::GetAICHReqDetails(this);
1620 uint16 nPart = data.ReadUInt16();
1621 if (pPartFile != NULL && request.m_pPartFile == pPartFile && request.m_pClient.GetClient() == this && nPart == request.m_nPart){
1622 CAICHHash ahMasterHash(&data);
1623 if ( (pPartFile->GetAICHHashset()->GetStatus() == AICH_TRUSTED || pPartFile->GetAICHHashset()->GetStatus() == AICH_VERIFIED)
1624 && ahMasterHash == pPartFile->GetAICHHashset()->GetMasterHash())
1625 {
1626 if(pPartFile->GetAICHHashset()->ReadRecoveryData(request.m_nPart*PARTSIZE, &data)){
1627 // finally all checks passed, everythings seem to be fine
1628 AddDebugLogLineN(logAICHTransfer, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1629 CAICHHashSet::RemoveClientAICHRequest(this);
1630 pPartFile->AICHRecoveryDataAvailable(request.m_nPart);
1631 return;
1632 } else {
1633 AddDebugLogLineN(logAICHTransfer, wxT("AICH Packet Answer: Succeeded to read and validate received recoverydata"));
1634 }
1635 } else {
1636 AddDebugLogLineN( logAICHTransfer, wxT("AICH Packet Answer: Masterhash differs from packethash or hashset has no trusted Masterhash") );
1637 }
1638 } else {
1639 AddDebugLogLineN( logAICHTransfer, wxT("AICH Packet Answer: requested values differ from values in packet") );
1640 }
1641
1642 CAICHHashSet::ClientAICHRequestFailed(this);
1643 }
1644
1645
ProcessAICHRequest(const uint8_t * packet,uint32 size)1646 void CUpDownClient::ProcessAICHRequest(const uint8_t* packet, uint32 size)
1647 {
1648 if (size != 16 + 2 + CAICHHash::GetHashSize()) {
1649 throw wxString(wxT("Received AICH Request Packet with wrong size"));
1650 }
1651
1652 CMemFile data(packet, size);
1653
1654 CMD4Hash hash = data.ReadHash();
1655 uint16 nPart = data.ReadUInt16();
1656 CAICHHash ahMasterHash(&data);
1657 CKnownFile* pKnownFile = theApp->sharedfiles->GetFileByID(hash);
1658 if (pKnownFile != NULL){
1659 if (pKnownFile->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE && pKnownFile->GetAICHHashset()->HasValidMasterHash()
1660 && pKnownFile->GetAICHHashset()->GetMasterHash() == ahMasterHash && pKnownFile->GetPartCount() > nPart
1661 && pKnownFile->GetFileSize() > EMBLOCKSIZE && pKnownFile->GetFileSize() - PARTSIZE*nPart > EMBLOCKSIZE)
1662 {
1663 CMemFile fileResponse;
1664 fileResponse.WriteHash(pKnownFile->GetFileHash());
1665 fileResponse.WriteUInt16(nPart);
1666 pKnownFile->GetAICHHashset()->GetMasterHash().Write(&fileResponse);
1667 if (pKnownFile->GetAICHHashset()->CreatePartRecoveryData(nPart*PARTSIZE, &fileResponse)){
1668 AddDebugLogLineN(logAICHTransfer,
1669 CFormat(wxT("AICH Packet Request: Sucessfully created and send recoverydata for '%s' to %s"))
1670 % pKnownFile->GetFileName() % GetClientFullInfo());
1671
1672 CPacket* packAnswer = new CPacket(fileResponse, OP_EMULEPROT, OP_AICHANSWER);
1673 theStats::AddUpOverheadOther(packAnswer->GetPacketSize());
1674 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1675 SafeSendPacket(packAnswer);
1676 return;
1677 } else {
1678 AddDebugLogLineN(logAICHTransfer,
1679 CFormat(wxT("AICH Packet Request: Failed to create recoverydata for '%s' to %s"))
1680 % pKnownFile->GetFileName() % GetClientFullInfo());
1681 }
1682 } else {
1683 AddDebugLogLineN(logAICHTransfer,
1684 CFormat(wxT("AICH Packet Request: Failed to create recoverydata - Hashset not ready or requested Hash differs from Masterhash for '%s' to %s"))
1685 % pKnownFile->GetFileName() % GetClientFullInfo());
1686 }
1687 } else {
1688 AddDebugLogLineN( logAICHTransfer, wxT("AICH Packet Request: Failed to find requested shared file - ") + GetClientFullInfo() );
1689 }
1690
1691 CPacket* packAnswer = new CPacket(OP_AICHANSWER, 16, OP_EMULEPROT);
1692 packAnswer->Copy16ToDataBuffer(hash.GetHash());
1693 theStats::AddUpOverheadOther(packAnswer->GetPacketSize());
1694 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_AICHANSWER to") + GetFullIP());
1695 SafeSendPacket(packAnswer);
1696 }
1697
ProcessAICHFileHash(CMemFile * data,const CPartFile * file)1698 void CUpDownClient::ProcessAICHFileHash(CMemFile* data, const CPartFile* file){
1699 CPartFile* pPartFile;
1700 if (file == NULL){
1701 pPartFile = theApp->downloadqueue->GetFileByID(data->ReadHash());
1702 } else {
1703 pPartFile = const_cast<CPartFile*>(file);
1704 }
1705 CAICHHash ahMasterHash(data);
1706
1707 if(pPartFile != NULL && pPartFile == GetRequestFile()){
1708 SetReqFileAICHHash(new CAICHHash(ahMasterHash));
1709 pPartFile->GetAICHHashset()->UntrustedHashReceived(ahMasterHash, GetConnectIP());
1710 } else {
1711 AddDebugLogLineN( logAICHTransfer, wxT("ProcessAICHFileHash(): PartFile not found or Partfile differs from requested file, ") + GetClientFullInfo() );
1712 }
1713 }
1714 // File_checked_for_headers
1715