2 // This file is part of the aMule Project.
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
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
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "updownclient.h" // Interface
28 #include <protocol/Protocols.h>
29 #include <protocol/ed2k/Client2Client/TCP.h>
33 #include "ClientCredits.h" // Needed for CClientCredits
34 #include "Packet.h" // Needed for CPacket
35 #include "MemFile.h" // Needed for CMemFile
36 #include "UploadQueue.h" // Needed for CUploadQueue
37 #include "DownloadQueue.h" // Needed for CDownloadQueue
38 #include "PartFile.h" // Needed for PR_POWERSHARE
39 #include "ClientTCPSocket.h" // Needed for CClientTCPSocket
40 #include "SharedFileList.h" // Needed for CSharedFileList
41 #include "amule.h" // Needed for theApp
42 #include "ClientList.h"
43 #include "Statistics.h" // Needed for theStats
45 #include "ScopedPtr.h" // Needed for CScopedArray
46 #include "GuiEvents.h" // Needed for Notify_*
47 #include "FileArea.h" // Needed for CFileArea
50 // members of CUpDownClient
51 // which are mainly used for uploading functions
53 void CUpDownClient::SetUploadState(uint8 eNewState
)
55 if (eNewState
!= m_nUploadState
) {
56 if (m_nUploadState
== US_UPLOADING
) {
57 // Reset upload data rate computation
59 m_nSumForAvgUpDataRate
= 0;
60 m_AvarageUDR_list
.clear();
62 if (eNewState
== US_UPLOADING
) {
63 m_fSentOutOfPartReqs
= 0;
66 // don't add any final cleanups for US_NONE here
67 m_nUploadState
= eNewState
;
68 UpdateDisplayedInfo(true);
73 uint32
CUpDownClient::GetScore(bool sysvalue
, bool isdownloading
, bool onlybasevalue
) const
75 //TODO: complete this (friends, uploadspeed, amuleuser etc etc)
76 if (m_Username
.IsEmpty()) {
84 const CKnownFile
* pFile
= GetUploadFile();
89 // bad clients (see note in function)
95 if (IsFriend() && GetFriendSlot() && !HasLowID()) {
102 if (sysvalue
&& HasLowID() && !IsConnected()){
106 // TODO coded by tecxx & herbert, one yet unsolved problem here:
107 // sometimes a client asks for 2 files and there is no way to decide, which file the
108 // client finally gets. so it could happen that he is queued first because of a
109 // high prio file, but then asks for something completely different.
110 int filepriority
= 10; // standard
112 switch(pFile
->GetUpPriority()) {
113 case PR_POWERSHARE
: //added for powershare (deltaHF)
134 // calculate score, based on waitingtime and other factors
138 } else if (!isdownloading
) {
139 fBaseValue
= (float)(::GetTickCount()-GetWaitStartTime())/1000;
141 // we dont want one client to download forever
142 // the first 15 min downloadtime counts as 15 min waitingtime and you get
143 // a 15 min bonus while you are in the first 15 min :)
144 // (to avoid 20 sec downloads) after this the score won't raise anymore
145 fBaseValue
= (float)(m_dwUploadTime
-GetWaitStartTime());
146 wxASSERT(fBaseValue
>= 0); // Obviously
147 fBaseValue
+= (float)(::GetTickCount() - m_dwUploadTime
> 900000)? 900000:1800000;
151 float modif
= GetScoreRatio();
154 if (!onlybasevalue
) {
155 fBaseValue
*= (float(filepriority
)/10.0f
);
157 if( (IsEmuleClient() || GetClientSoft() < 10) && m_byEmuleVersion
<= 0x19) {
160 return (uint32
)fBaseValue
;
164 // Checks if it is next requested block from another chunk of the actual file or from another file
167 // true : Next requested block is from another different chunk or file than last downloaded block
168 // false: Next requested block is from same chunk that last downloaded block
169 bool CUpDownClient::IsDifferentPartBlock() const // [Tarod 12/22/2002]
171 bool different_part
= false;
173 // Check if we have good lists and proceed to check for different chunks
174 if ((!m_BlockRequests_queue
.empty()) && !m_DoneBlocks_list
.empty())
176 Requested_Block_Struct
* last_done_block
= NULL
;
177 Requested_Block_Struct
* next_requested_block
= NULL
;
178 uint64 last_done_part
= 0xffffffff;
179 uint64 next_requested_part
= 0xffffffff;
182 // Get last block and next pending
183 last_done_block
= m_DoneBlocks_list
.front();
184 next_requested_block
= m_BlockRequests_queue
.front();
186 // Calculate corresponding parts to blocks
187 last_done_part
= last_done_block
->StartOffset
/ PARTSIZE
;
188 next_requested_part
= next_requested_block
->StartOffset
/ PARTSIZE
;
190 // Test is we are asking same file and same part
191 if ( last_done_part
!= next_requested_part
) {
192 different_part
= true;
193 AddDebugLogLineM(false, logClient
, wxT("Session ended due to new chunk."));
196 if (md4cmp(last_done_block
->FileID
, next_requested_block
->FileID
) != 0) {
197 different_part
= true;
198 AddDebugLogLineM(false, logClient
, wxT("Session ended due to different file."));
202 return different_part
;
206 void CUpDownClient::CreateNextBlockPackage()
209 // Buffer new data if current buffer is less than 100 KBytes
210 while (!m_BlockRequests_queue
.empty()
211 && m_addedPayloadQueueSession
- m_nCurQueueSessionPayloadUp
< 100*1024) {
213 Requested_Block_Struct
* currentblock
= m_BlockRequests_queue
.front();
214 CKnownFile
* srcfile
= theApp
->sharedfiles
->GetFileByID(CMD4Hash(currentblock
->FileID
));
217 throw wxString(wxT("requested file not found"));
220 // Check if this know file is a CPartFile.
221 // For completed part files IsPartFile() returns false, so they are
222 // correctly treated as plain CKnownFile.
223 CPartFile
* srcPartFile
= srcfile
->IsPartFile() ? (CPartFile
*)srcfile
: NULL
;
225 // THIS EndOffset points BEHIND the last byte requested
226 // (other than the offsets used in the PartFile code)
227 if (currentblock
->EndOffset
> srcfile
->GetFileSize()) {
228 throw wxString(CFormat(wxT("Asked for data up to %d beyond end of file (%d)"))
229 % currentblock
->EndOffset
% srcfile
->GetFileSize());
230 } else if (currentblock
->StartOffset
> currentblock
->EndOffset
) {
231 throw wxString(CFormat(wxT("Asked for invalid block (start %d > end %d)"))
232 % currentblock
->StartOffset
% currentblock
->EndOffset
);
235 uint64 togo
= currentblock
->EndOffset
- currentblock
->StartOffset
;
237 if (togo
> EMBLOCKSIZE
* 3) {
238 throw wxString(CFormat(wxT("Client requested too large block (%d > %d)"))
239 % togo
% (EMBLOCKSIZE
* 3));
244 if (!srcPartFile
->IsComplete(currentblock
->StartOffset
,currentblock
->EndOffset
-1)) {
245 throw wxString(CFormat(wxT("Asked for incomplete block (%d - %d)"))
246 % currentblock
->StartOffset
% (currentblock
->EndOffset
-1));
248 if (!srcPartFile
->ReadData(area
, currentblock
->StartOffset
, togo
)) {
249 throw wxString(wxT("Failed to read from requested partfile"));
253 CPath fullname
= srcfile
->GetFilePath().JoinPaths(srcfile
->GetFileName());
254 if ( !file
.Open(fullname
, CFile::read
) ) {
255 // The file was most likely moved/deleted. So remove it from the list of shared files.
256 AddLogLineM( false, CFormat( _("Failed to open file (%s), removing from list of shared files.") ) % srcfile
->GetFileName() );
257 theApp
->sharedfiles
->RemoveFile(srcfile
);
259 throw wxString(wxT("Failed to open requested file: Removing from list of shared files!"));
261 area
.ReadAt(file
, currentblock
->StartOffset
, togo
);
265 SetUploadFileID(srcfile
);
267 // check extention to decide whether to compress or not
268 if (m_byDataCompVer
== 1 && GetFiletype(srcfile
->GetFileName()) != ftArchive
) {
269 CreatePackedPackets(area
.GetBuffer(), togo
, currentblock
);
271 CreateStandardPackets(area
.GetBuffer(), togo
, currentblock
);
275 srcfile
->statistic
.AddTransferred(togo
);
277 m_addedPayloadQueueSession
+= togo
;
279 Requested_Block_Struct
* block
= m_BlockRequests_queue
.front();
281 m_BlockRequests_queue
.pop_front();
282 m_DoneBlocks_list
.push_front(block
);
286 } catch (const wxString
& error
) {
287 AddDebugLogLineM(false, logClient
,
288 CFormat(wxT("Client '%s' (%s) caused error while creating packet (%s) - disconnecting client"))
289 % GetUserName() % GetFullIP() % error
);
290 } catch (const CIOFailureException
& error
) {
291 AddDebugLogLineM(true, logClient
, wxT("IO failure while reading requested file: ") + error
.what());
292 } catch (const CEOFException
& WXUNUSED(error
)) {
293 AddDebugLogLineM(true, logClient
, GetClientFullInfo() + wxT(" requested file-data at an invalid position - disconnecting"));
297 theApp
->uploadqueue
->RemoveFromUploadQueue(this);
301 void CUpDownClient::CreateStandardPackets(const byte
* buffer
, uint32 togo
, Requested_Block_Struct
* currentblock
)
305 CMemFile
memfile(buffer
, togo
);
307 nPacketSize
= togo
/(uint32
)(togo
/10240);
313 if (togo
< nPacketSize
*2) {
317 wxASSERT(nPacketSize
);
320 uint64 endpos
= (currentblock
->EndOffset
- togo
);
321 uint64 startpos
= endpos
- nPacketSize
;
323 bool bLargeBlocks
= (startpos
> 0xFFFFFFFF) || (endpos
> 0xFFFFFFFF);
325 CMemFile
data(nPacketSize
+ 16 + 2 * (bLargeBlocks
? 8 :4));
326 data
.WriteHash(GetUploadFileID());
328 data
.WriteUInt64(startpos
);
329 data
.WriteUInt64(endpos
);
331 data
.WriteUInt32(startpos
);
332 data
.WriteUInt32(endpos
);
334 char *tempbuf
= new char[nPacketSize
];
335 memfile
.Read(tempbuf
, nPacketSize
);
336 data
.Write(tempbuf
, nPacketSize
);
338 CPacket
* packet
= new CPacket(data
, (bLargeBlocks
? OP_EMULEPROT
: OP_EDONKEYPROT
), (bLargeBlocks
? (uint8
)OP_SENDINGPART_I64
: (uint8
)OP_SENDINGPART
));
339 theStats::AddUpOverheadFileRequest(16 + 2 * (bLargeBlocks
? 8 :4));
340 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize
);
341 AddDebugLogLineM(false, logLocalClient
,
342 CFormat(wxT("Local Client: %s to %s"))
343 % (bLargeBlocks
? wxT("OP_SENDINGPART_I64") : wxT("OP_SENDINGPART")) % GetFullIP() );
344 m_socket
->SendPacket(packet
,true,false, nPacketSize
);
349 void CUpDownClient::CreatePackedPackets(const byte
* buffer
, uint32 togo
, Requested_Block_Struct
* currentblock
)
351 uLongf newsize
= togo
+300;
352 CScopedArray
<byte
> output(newsize
);
353 uint16 result
= compress2(output
.get(), &newsize
, buffer
, togo
, 9);
354 if (result
!= Z_OK
|| togo
<= newsize
){
355 CreateStandardPackets(buffer
, togo
, currentblock
);
359 CMemFile
memfile(output
.get(), newsize
);
361 uint32 totalPayloadSize
= 0;
362 uint32 oldSize
= togo
;
366 nPacketSize
= togo
/(uint32
)(togo
/10240);
372 if (togo
< nPacketSize
*2) {
377 bool isLargeBlock
= (currentblock
->StartOffset
> 0xFFFFFFFF) || (currentblock
->EndOffset
> 0xFFFFFFFF);
379 CMemFile
data(nPacketSize
+ 16 + (isLargeBlock
? 12 : 8));
380 data
.WriteHash(GetUploadFileID());
382 data
.WriteUInt64(currentblock
->StartOffset
);
384 data
.WriteUInt32(currentblock
->StartOffset
);
386 data
.WriteUInt32(newsize
);
387 char *tempbuf
= new char[nPacketSize
];
388 memfile
.Read(tempbuf
, nPacketSize
);
389 data
.Write(tempbuf
,nPacketSize
);
391 CPacket
* packet
= new CPacket(data
, OP_EMULEPROT
, (isLargeBlock
? OP_COMPRESSEDPART_I64
: OP_COMPRESSEDPART
));
393 // approximate payload size
394 uint32 payloadSize
= nPacketSize
*oldSize
/newsize
;
396 if (togo
== 0 && totalPayloadSize
+payloadSize
< oldSize
) {
397 payloadSize
= oldSize
-totalPayloadSize
;
400 totalPayloadSize
+= payloadSize
;
402 // put packet directly on socket
403 theStats::AddUpOverheadFileRequest(24);
404 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize
);
405 AddDebugLogLineM(false, logLocalClient
,
406 CFormat(wxT("Local Client: %s to %s"))
407 % (isLargeBlock
? wxT("OP_COMPRESSEDPART_I64") : wxT("OP_COMPRESSEDPART")) % GetFullIP() );
408 m_socket
->SendPacket(packet
,true,false, payloadSize
);
413 void CUpDownClient::ProcessExtendedInfo(const CMemFile
*data
, CKnownFile
*tempreqfile
)
415 m_uploadingfile
->UpdateUpPartsFrequency( this, false ); // Decrement
416 m_upPartStatus
.clear();
417 m_nUpCompleteSourcesCount
= 0;
419 if( GetExtendedRequestsVersion() == 0 ) {
420 // Something is coded wrong on this client if he's sending something it doesn't advertise.
424 if (data
->GetLength() == 16) {
425 // Wrong again. Advertised >0 but send a 0-type packet.
426 // But this time we'll disconnect it.
427 throw CInvalidPacket(wxT("Wrong size on extended info packet"));
430 uint16 nED2KUpPartCount
= data
->ReadUInt16();
431 if (!nED2KUpPartCount
) {
432 m_upPartStatus
.setsize( tempreqfile
->GetPartCount(), 0 );
434 if (tempreqfile
->GetED2KPartCount() != nED2KUpPartCount
) {
435 // We already checked if we are talking about the same file.. So if we get here, something really strange happened!
436 m_upPartStatus
.clear();
440 m_upPartStatus
.setsize( tempreqfile
->GetPartCount(), 0 );
444 while (done
!= m_upPartStatus
.size()) {
445 uint8 toread
= data
->ReadUInt8();
446 for (sint32 i
= 0;i
!= 8;i
++){
447 m_upPartStatus
.set(done
, (toread
>>i
)&1);
448 // We may want to use this for another feature..
449 // if (m_upPartStatus[done] && !tempreqfile->IsComplete(done*PARTSIZE,((done+1)*PARTSIZE)-1))
450 // bPartsNeeded = true;
452 if (done
== m_upPartStatus
.size()) {
458 // We want the increment the frequency even if we didn't read everything
459 m_uploadingfile
->UpdateUpPartsFrequency( this, true ); // Increment
464 if (GetExtendedRequestsVersion() > 1) {
465 uint16 nCompleteCountLast
= GetUpCompleteSourcesCount();
466 uint16 nCompleteCountNew
= data
->ReadUInt16();
467 SetUpCompleteSourcesCount(nCompleteCountNew
);
468 if (nCompleteCountLast
!= nCompleteCountNew
) {
469 tempreqfile
->UpdatePartsInfo();
474 m_uploadingfile
->UpdateUpPartsFrequency( this, true ); // Increment
476 Notify_SharedCtrlRefreshClient(this, AVAILABLE_SOURCE
);
480 void CUpDownClient::SetUploadFileID(CKnownFile
* newreqfile
)
482 if (m_uploadingfile
== newreqfile
) {
484 } else if (m_uploadingfile
) {
485 m_uploadingfile
->RemoveUploadingClient(this);
486 m_uploadingfile
->UpdateUpPartsFrequency(this, false); // Decrement
490 // This is a new file! update info
491 newreqfile
->AddUploadingClient(this);
493 if (m_requpfileid
!= newreqfile
->GetFileHash()) {
494 m_requpfileid
= newreqfile
->GetFileHash();
495 m_upPartStatus
.setsize( newreqfile
->GetPartCount(), 0 );
497 // this is the same file we already had assigned. Only update data.
498 newreqfile
->UpdateUpPartsFrequency(this, true); // Increment
501 m_uploadingfile
= newreqfile
;
503 m_upPartStatus
.clear();
504 m_nUpCompleteSourcesCount
= 0;
505 // This clears m_uploadingfile and m_requpfileid
511 void CUpDownClient::AddReqBlock(Requested_Block_Struct
* reqblock
)
513 if (GetUploadState() != US_UPLOADING
) {
514 AddDebugLogLineM(false, logRemoteClient
, wxT("UploadClient: Client tried to add requested block when not in upload slot! Prevented requested blocks from being added."));
520 std::list
<Requested_Block_Struct
*>::iterator it
= m_DoneBlocks_list
.begin();
521 for (; it
!= m_DoneBlocks_list
.end(); ++it
) {
522 if (reqblock
->StartOffset
== (*it
)->StartOffset
&& reqblock
->EndOffset
== (*it
)->EndOffset
) {
530 std::list
<Requested_Block_Struct
*>::iterator it
= m_BlockRequests_queue
.begin();
531 for (; it
!= m_BlockRequests_queue
.end(); ++it
) {
532 if (reqblock
->StartOffset
== (*it
)->StartOffset
&& reqblock
->EndOffset
== (*it
)->EndOffset
) {
539 m_BlockRequests_queue
.push_back(reqblock
);
543 uint32
CUpDownClient::GetWaitStartTime() const
548 dwResult
= credits
->GetSecureWaitStartTime(GetIP());
550 if (dwResult
> m_dwUploadTime
&& IsDownloading()) {
551 // This happens only if two clients with invalid securehash are in the queue - if at all
552 dwResult
= m_dwUploadTime
- 1;
560 void CUpDownClient::SetWaitStartTime()
563 credits
->SetSecWaitStartTime(GetIP());
568 void CUpDownClient::ClearWaitStartTime()
571 credits
->ClearWaitStartTime();
576 void CUpDownClient::ResetSessionUp()
578 m_nCurSessionUp
= m_nTransferredUp
;
579 m_addedPayloadQueueSession
= 0;
580 m_nCurQueueSessionPayloadUp
= 0;
581 // If upload was resumed there can be a remaining payload in the socket
582 // causing (prepared - sent) getting negative. So reset the counter here.
584 CEMSocket
* s
= m_socket
;
585 s
->GetSentPayloadSinceLastCallAndReset();
590 uint32
CUpDownClient::SendBlockData()
592 uint32 curTick
= ::GetTickCount();
593 uint64 sentBytesCompleteFile
= 0;
594 uint64 sentBytesPartFile
= 0;
595 uint64 sentBytesPayload
= 0;
598 CEMSocket
* s
= m_socket
;
599 // uint32 uUpStatsPort = GetUserPort();
601 // Extended statistics information based on which client software and which port we sent this data to...
602 // This also updates the grand total for sent bytes, etc. And where this data came from.
603 sentBytesCompleteFile
= s
->GetSentBytesCompleteFileSinceLastCallAndReset();
604 sentBytesPartFile
= s
->GetSentBytesPartFileSinceLastCallAndReset();
605 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, false, true, sentBytesCompleteFile, (IsFriend() && GetFriendSlot()));
606 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, true, true, sentBytesPartFile, (IsFriend() && GetFriendSlot()));
608 m_nTransferredUp
+= sentBytesCompleteFile
+ sentBytesPartFile
;
609 credits
->AddUploaded(sentBytesCompleteFile
+ sentBytesPartFile
, GetIP(), theApp
->CryptoAvailable());
611 sentBytesPayload
= s
->GetSentPayloadSinceLastCallAndReset();
612 m_nCurQueueSessionPayloadUp
+= sentBytesPayload
;
614 if (theApp
->uploadqueue
->CheckForTimeOver(this)) {
615 theApp
->uploadqueue
->RemoveFromUploadQueue(this);
616 SendOutOfPartReqsAndAddToWaitingQueue();
618 // read blocks from file and put on socket
619 CreateNextBlockPackage();
623 if(sentBytesCompleteFile
+ sentBytesPartFile
> 0 ||
624 m_AvarageUDR_list
.empty() || (curTick
- m_AvarageUDR_list
.back().timestamp
) > 1*1000) {
625 // Store how much data we've transferred this round,
626 // to be able to calculate average speed later
627 // keep sum of all values in list up to date
628 TransferredData newitem
= {sentBytesCompleteFile
+ sentBytesPartFile
, curTick
};
629 m_AvarageUDR_list
.push_back(newitem
);
630 m_nSumForAvgUpDataRate
+= sentBytesCompleteFile
+ sentBytesPartFile
;
633 // remove to old values in list
634 while ((!m_AvarageUDR_list
.empty()) && (curTick
- m_AvarageUDR_list
.front().timestamp
) > 10*1000) {
635 // keep sum of all values in list up to date
636 m_nSumForAvgUpDataRate
-= m_AvarageUDR_list
.front().datalen
;
637 m_AvarageUDR_list
.pop_front();
640 // Calculate average speed for this slot
641 if ((!m_AvarageUDR_list
.empty()) && (curTick
- m_AvarageUDR_list
.front().timestamp
) > 0 && GetUpStartTimeDelay() > 2*1000) {
642 m_nUpDatarate
= ((uint64
)m_nSumForAvgUpDataRate
*1000) / (curTick
-m_AvarageUDR_list
.front().timestamp
);
644 // not enough values to calculate trustworthy speed. Use -1 to tell this
645 m_nUpDatarate
= 0; //-1;
648 // Check if it's time to update the display.
650 if (m_cSendblock
== 30){
652 Notify_SharedCtrlRefreshClient(this, AVAILABLE_SOURCE
);
655 return sentBytesCompleteFile
+ sentBytesPartFile
;
659 void CUpDownClient::SendOutOfPartReqsAndAddToWaitingQueue()
661 // Kry - this is actually taken from eMule, but makes a lot of sense ;)
663 //OP_OUTOFPARTREQS will tell the downloading client to go back to OnQueue..
664 //The main reason for this is that if we put the client back on queue and it goes
665 //back to the upload before the socket times out... We get a situation where the
666 //downloader thinks it already sent the requested blocks and the uploader thinks
667 //the downloader didn't send any request blocks. Then the connection times out..
668 //I did some tests with eDonkey also and it seems to work well with them also..
670 // Send this inmediately, don't queue.
671 CPacket
* pPacket
= new CPacket(OP_OUTOFPARTREQS
, 0, OP_EDONKEYPROT
);
672 theStats::AddUpOverheadFileRequest(pPacket
->GetPacketSize());
673 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_OUTOFPARTREQS to ") + GetFullIP() );
674 SendPacket(pPacket
, true, true);
676 theApp
->uploadqueue
->AddClientToQueue(this);
681 * See description for CEMSocket::TruncateQueues().
683 void CUpDownClient::FlushSendBlocks()
685 // Call this when you stop upload, or the socket might be not able to send
686 if (m_socket
) { //socket may be NULL...
687 m_socket
->TruncateQueues();
692 void CUpDownClient::SendHashsetPacket(const CMD4Hash
& forfileid
)
694 CKnownFile
* file
= theApp
->sharedfiles
->GetFileByID( forfileid
);
695 bool from_dq
= false;
698 if ((file
= theApp
->downloadqueue
->GetFileByID(forfileid
)) == NULL
) {
699 AddLogLineM(false, CFormat( _("Hashset requested for unknown file: %s") ) % forfileid
.Encode() );
705 if ( !file
->GetHashCount() ) {
707 AddDebugLogLineM(false, logRemoteClient
, wxT("Requested hashset could not be found"));
710 file
= theApp
->downloadqueue
->GetFileByID(forfileid
);
711 if (!(file
&& file
->GetHashCount())) {
712 AddDebugLogLineM(false, logRemoteClient
, wxT("Requested hashset could not be found"));
719 data
.WriteHash(file
->GetFileHash());
720 uint16 parts
= file
->GetHashCount();
721 data
.WriteUInt16(parts
);
722 for (int i
= 0; i
!= parts
; i
++) {
723 data
.WriteHash(file
->GetPartHash(i
));
725 CPacket
* packet
= new CPacket(data
, OP_EDONKEYPROT
, OP_HASHSETANSWER
);
726 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
727 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_HASHSETANSWER to ") + GetFullIP());
728 SendPacket(packet
,true,true);
732 void CUpDownClient::ClearUploadBlockRequests()
735 DeleteContents(m_BlockRequests_queue
);
736 DeleteContents(m_DoneBlocks_list
);
739 uint16
CUpDownClient::GetRankingInfo() const {
740 return theApp
->uploadqueue
->GetWaitingPosition(this);
743 void CUpDownClient::SendRankingInfo(){
744 if (!ExtProtocolAvailable()) {
748 uint16 nRank
= GetRankingInfo();
754 data
.WriteUInt16(nRank
);
755 // Kry: what are these zero bytes for. are they really correct?
756 // Kry - Well, eMule does like that. I guess they're ok.
757 data
.WriteUInt32(0); data
.WriteUInt32(0); data
.WriteUInt16(0);
758 CPacket
* packet
= new CPacket(data
, OP_EMULEPROT
, OP_QUEUERANKING
);
759 theStats::AddUpOverheadOther(packet
->GetPacketSize());
760 AddDebugLogLineM(false, logLocalClient
, wxT("Local Client: OP_QUEUERANKING to ") + GetFullIP());
761 SendPacket(packet
,true,true);
765 void CUpDownClient::SendCommentInfo(CKnownFile
* file
)
767 if (!m_bCommentDirty
|| file
== NULL
|| !ExtProtocolAvailable() || m_byAcceptCommentVer
< 1) {
770 m_bCommentDirty
= false;
772 // Truncate to max len.
773 wxString desc
= file
->GetFileComment().Left(MAXFILECOMMENTLEN
);
774 uint8 rating
= file
->GetFileRating();
776 if ( file
->GetFileRating() == 0 && desc
.IsEmpty() ) {
781 data
.WriteUInt8(rating
);
782 data
.WriteString(desc
, GetUnicodeSupport(), 4 /* size it's uint32 */);
784 CPacket
* packet
= new CPacket(data
, OP_EMULEPROT
, OP_FILEDESC
);
785 theStats::AddUpOverheadOther(packet
->GetPacketSize());
786 AddDebugLogLineM(false, logLocalClient
, wxT("Local Client: OP_FILEDESC to ") + GetFullIP());
787 SendPacket(packet
,true);
790 void CUpDownClient::UnBan(){
791 m_Aggressiveness
= 0;
793 theApp
->clientlist
->AddTrackClient(this);
794 theApp
->clientlist
->RemoveBannedClient( GetIP() );
795 SetUploadState(US_NONE
);
796 ClearWaitStartTime();
798 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
801 void CUpDownClient::Ban(){
802 theApp
->clientlist
->AddTrackClient(this);
803 theApp
->clientlist
->AddBannedClient( GetIP() );
805 AddDebugLogLineM( false, logClient
, wxT("Client '") + GetUserName() + wxT("' seems to be an aggressive client and is banned from the uploadqueue"));
807 SetUploadState(US_BANNED
);
809 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
810 Notify_SharedCtrlRefreshClient(this, UNAVAILABLE_SOURCE
);
813 bool CUpDownClient::IsBanned() const
815 return ( (theApp
->clientlist
->IsBannedClient(GetIP()) ) && m_nDownloadState
!= DS_DOWNLOADING
);
818 void CUpDownClient::CheckForAggressive()
820 uint32 cur_time
= ::GetTickCount();
822 // First call, initalize
823 if ( !m_LastFileRequest
) {
824 m_LastFileRequest
= cur_time
;
828 // Is this an aggressive request?
829 if ( ( cur_time
- m_LastFileRequest
) < MIN_REQUESTTIME
) {
830 m_Aggressiveness
+= 3;
832 // Is the client EVIL?
833 if ( m_Aggressiveness
>= 10 && (!IsBanned() && m_nDownloadState
!= DS_DOWNLOADING
)) {
834 AddDebugLogLineM( false, logClient
, CFormat( wxT("Aggressive client banned (score: %d): %s -- %s -- %s") )
838 % m_fullClientVerString
);
842 // Polite request, reward client
843 if ( m_Aggressiveness
)
847 m_LastFileRequest
= cur_time
;
851 void CUpDownClient::SetUploadFileID(const CMD4Hash
& new_id
)
853 // Update the uploading file found
854 CKnownFile
* uploadingfile
= theApp
->sharedfiles
->GetFileByID(new_id
);
855 if ( !uploadingfile
) {
856 // Can this really happen?
857 uploadingfile
= theApp
->downloadqueue
->GetFileByID(new_id
);
859 SetUploadFileID(uploadingfile
); // This will update queue count on old and new file.
862 void CUpDownClient::ProcessRequestPartsPacket(const byte
* pachPacket
, uint32 nSize
, bool largeblocks
) {
864 CMemFile
data(pachPacket
, nSize
);
866 CMD4Hash reqfilehash
= data
.ReadHash();
868 uint64 auStartOffsets
[3];
869 uint64 auEndOffsets
[3];
872 auStartOffsets
[0] = data
.ReadUInt64();
873 auStartOffsets
[1] = data
.ReadUInt64();
874 auStartOffsets
[2] = data
.ReadUInt64();
876 auEndOffsets
[0] = data
.ReadUInt64();
877 auEndOffsets
[1] = data
.ReadUInt64();
878 auEndOffsets
[2] = data
.ReadUInt64();
880 auStartOffsets
[0] = data
.ReadUInt32();
881 auStartOffsets
[1] = data
.ReadUInt32();
882 auStartOffsets
[2] = data
.ReadUInt32();
884 auEndOffsets
[0] = data
.ReadUInt32();
885 auEndOffsets
[1] = data
.ReadUInt32();
886 auEndOffsets
[2] = data
.ReadUInt32();
889 for (unsigned int i
= 0; i
< itemsof(auStartOffsets
); i
++) {
890 AddDebugLogLineM(false, logClient
,
891 CFormat(wxT("Client %s requests %d File block %d-%d (%d bytes):"))
892 % GetFullIP() % i
% auStartOffsets
[i
] % auEndOffsets
[i
]
893 % (auEndOffsets
[i
] - auStartOffsets
[i
]));
894 if (auEndOffsets
[i
] > auStartOffsets
[i
]) {
895 Requested_Block_Struct
* reqblock
= new Requested_Block_Struct
;
896 reqblock
->StartOffset
= auStartOffsets
[i
];
897 reqblock
->EndOffset
= auEndOffsets
[i
];
898 md4cpy(reqblock
->FileID
, reqfilehash
.GetHash());
899 reqblock
->transferred
= 0;
900 AddReqBlock(reqblock
);
902 if (auEndOffsets
[i
] != 0 || auStartOffsets
[i
] != 0) {
903 AddDebugLogLineM(false, logClient
, wxT("Client request is invalid!"));
909 void CUpDownClient::ProcessRequestPartsPacketv2(const CMemFile
& data
) {
911 CMD4Hash reqfilehash
= data
.ReadHash();
913 uint8 numblocks
= data
.ReadUInt8();
915 for (int i
= 0; i
< numblocks
; i
++) {
916 Requested_Block_Struct
* reqblock
= new Requested_Block_Struct
;
918 reqblock
->StartOffset
= data
.GetIntTagValue();
919 // We have to do +1, because the block matching uses that.
920 reqblock
->EndOffset
= data
.GetIntTagValue() + 1;
921 if ((reqblock
->StartOffset
|| reqblock
->EndOffset
) && (reqblock
->StartOffset
> reqblock
->EndOffset
)) {
922 AddDebugLogLineM(false, logClient
, CFormat(wxT("Client %s request is invalid! %d / %d"))
923 % GetFullIP() % reqblock
->StartOffset
% reqblock
->EndOffset
);
924 throw wxString(wxT("Client request is invalid!"));
927 AddDebugLogLineM(false, logClient
,
928 CFormat(wxT("Client %s requests %d File block %d-%d (%d bytes):"))
929 % GetFullIP() % i
% reqblock
->StartOffset
% reqblock
->EndOffset
930 % (reqblock
->EndOffset
- reqblock
->StartOffset
));
932 md4cpy(reqblock
->FileID
, reqfilehash
.GetHash());
933 reqblock
->transferred
= 0;
934 AddReqBlock(reqblock
);
941 // File_checked_for_headers