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 <common/Format.h>
46 #include "ScopedPtr.h" // Needed for CScopedArray
47 #include "GuiEvents.h" // Needed for Notify_*
48 #include "FileArea.h" // Needed for CFileArea
51 // members of CUpDownClient
52 // which are mainly used for uploading functions
54 void CUpDownClient::SetUploadState(uint8 eNewState
)
56 if (eNewState
!= m_nUploadState
) {
57 if (m_nUploadState
== US_UPLOADING
) {
58 // Reset upload data rate computation
60 m_nSumForAvgUpDataRate
= 0;
61 m_AvarageUDR_list
.clear();
63 if (eNewState
== US_UPLOADING
) {
64 m_fSentOutOfPartReqs
= 0;
67 // don't add any final cleanups for US_NONE here
68 m_nUploadState
= eNewState
;
69 UpdateDisplayedInfo(true);
74 uint32
CUpDownClient::GetScore(bool sysvalue
, bool isdownloading
, bool onlybasevalue
) const
76 //TODO: complete this (friends, uploadspeed, amuleuser etc etc)
77 if (m_Username
.IsEmpty()) {
85 const CKnownFile
* pFile
= GetUploadFile();
90 // bad clients (see note in function)
96 if (IsFriend() && GetFriendSlot() && !HasLowID()) {
103 if (sysvalue
&& HasLowID() && !IsConnected()){
107 // TODO coded by tecxx & herbert, one yet unsolved problem here:
108 // sometimes a client asks for 2 files and there is no way to decide, which file the
109 // client finally gets. so it could happen that he is queued first because of a
110 // high prio file, but then asks for something completely different.
111 int filepriority
= 10; // standard
113 switch(pFile
->GetUpPriority()) {
114 case PR_POWERSHARE
: //added for powershare (deltaHF)
135 // calculate score, based on waitingtime and other factors
139 } else if (!isdownloading
) {
140 fBaseValue
= (float)(::GetTickCount()-GetWaitStartTime())/1000;
142 // we dont want one client to download forever
143 // the first 15 min downloadtime counts as 15 min waitingtime and you get
144 // a 15 min bonus while you are in the first 15 min :)
145 // (to avoid 20 sec downloads) after this the score won't raise anymore
146 fBaseValue
= (float)(m_dwUploadTime
-GetWaitStartTime());
147 wxASSERT( m_dwUploadTime
> GetWaitStartTime()); // Obviously
148 fBaseValue
+= (float)(::GetTickCount() - m_dwUploadTime
> 900000)? 900000:1800000;
152 float modif
= GetScoreRatio();
155 if (!onlybasevalue
) {
156 fBaseValue
*= (float(filepriority
)/10.0f
);
158 if( (IsEmuleClient() || GetClientSoft() < 10) && m_byEmuleVersion
<= 0x19) {
161 return (uint32
)fBaseValue
;
165 // Checks if it is next requested block from another chunk of the actual file or from another file
168 // true : Next requested block is from another different chunk or file than last downloaded block
169 // false: Next requested block is from same chunk that last downloaded block
170 bool CUpDownClient::IsDifferentPartBlock() const // [Tarod 12/22/2002]
172 bool different_part
= false;
174 // Check if we have good lists and proceed to check for different chunks
175 if ((!m_BlockRequests_queue
.empty()) && !m_DoneBlocks_list
.empty())
177 Requested_Block_Struct
* last_done_block
= NULL
;
178 Requested_Block_Struct
* next_requested_block
= NULL
;
179 uint64 last_done_part
= 0xffffffff;
180 uint64 next_requested_part
= 0xffffffff;
183 // Get last block and next pending
184 last_done_block
= m_DoneBlocks_list
.front();
185 next_requested_block
= m_BlockRequests_queue
.front();
187 // Calculate corresponding parts to blocks
188 last_done_part
= last_done_block
->StartOffset
/ PARTSIZE
;
189 next_requested_part
= next_requested_block
->StartOffset
/ PARTSIZE
;
191 // Test is we are asking same file and same part
192 if ( last_done_part
!= next_requested_part
) {
193 different_part
= true;
194 AddDebugLogLineM(false, logClient
, wxT("Session ended due to new chunk."));
197 if (md4cmp(last_done_block
->FileID
, next_requested_block
->FileID
) != 0) {
198 different_part
= true;
199 AddDebugLogLineM(false, logClient
, wxT("Session ended due to different file."));
203 return different_part
;
207 void CUpDownClient::CreateNextBlockPackage()
209 // See if we can do an early return. There may be no new blocks to load from disk and add to buffer, or buffer may be large enough allready.
210 if(m_BlockRequests_queue
.empty() || // There are no new blocks requested
211 ((m_addedPayloadQueueSession
> GetQueueSessionPayloadUp()) && m_addedPayloadQueueSession
-GetQueueSessionPayloadUp() > 50*1024)) { // the buffered data is large enough allready
217 // Buffer new data if current buffer is less than 100 KBytes
218 while ((!m_BlockRequests_queue
.empty()) &&
219 (m_addedPayloadQueueSession
<= GetQueueSessionPayloadUp() || m_addedPayloadQueueSession
-GetQueueSessionPayloadUp() < 100*1024)) {
221 Requested_Block_Struct
* currentblock
= m_BlockRequests_queue
.front();
222 CKnownFile
* srcfile
= theApp
->sharedfiles
->GetFileByID(CMD4Hash(currentblock
->FileID
));
225 throw wxString(wxT("requested file not found"));
228 if (srcfile
->IsPartFile() && ((CPartFile
*)srcfile
)->GetStatus() != PS_COMPLETE
) {
229 //#warning This seems a good idea from eMule. We must import this.
231 // Do not access a part file, if it is currently moved into the incoming directory.
232 // Because the moving of part file into the incoming directory may take a noticable
233 // amount of time, we can not wait for 'm_FileCompleteMutex' and block the main thread.
234 if (!((CPartFile
*)srcfile
)->m_FileCompleteMutex
.Lock(0)){ // just do a quick test of the mutex's state and return if it's locked.
237 lockFile
.m_pObject
= &((CPartFile
*)srcfile
)->m_FileCompleteMutex
;
238 // If it's a part file which we are uploading the file remains locked until we've read the
239 // current block. This way the file completion thread can not (try to) "move" the file into
240 // the incoming directory.
243 // Get the full path to the '.part' file
244 fullname
= dynamic_cast<CPartFile
*>(srcfile
)->GetFullName().RemoveExt();
246 fullname
= srcfile
->GetFilePath().JoinPaths(srcfile
->GetFileName());
250 // THIS EndOffset points BEHIND the last byte requested
251 // (other than the offsets used in the PartFile code)
252 if (currentblock
->EndOffset
> srcfile
->GetFileSize()) {
253 throw wxString(wxT("Asked for data beyond end of file"));
254 } else if (currentblock
->StartOffset
> currentblock
->EndOffset
) {
255 throw wxString(wxT("Asked for invalid block (start > end)"));
257 togo
= currentblock
->EndOffset
- currentblock
->StartOffset
;
259 if (srcfile
->IsPartFile() && !((CPartFile
*)srcfile
)->IsComplete(currentblock
->StartOffset
,currentblock
->EndOffset
-1)) {
260 throw wxString(wxT("Asked for incomplete block "));
264 if (togo
> EMBLOCKSIZE
* 3) {
265 throw wxString(wxT("Client requested too large of a block."));
269 if (!srcfile
->IsPartFile()){
271 if ( !file
.Open(fullname
, CFile::read
) ) {
272 // The file was most likely moved/deleted. However it is likely that the
273 // same is true for other files, so we recheck all shared files.
274 AddLogLineM( false, CFormat( _("Failed to open file (%s), removing from list of shared files.") ) % srcfile
->GetFileName() );
275 theApp
->sharedfiles
->RemoveFile(srcfile
);
277 throw wxString(wxT("Failed to open requested file: Removing from list of shared files!"));
279 area
.Read(file
, togo
);
281 if (!((CPartFile
*)srcfile
)->ReadData(area
, currentblock
->StartOffset
, togo
))
282 throw wxString(wxT("Failed to read from requested partfile"));
286 //#warning Part of the above import.
288 if (lockFile
.m_pObject
){
289 lockFile
.m_pObject
->Unlock(); // Unlock the (part) file as soon as we are done with accessing it.
290 lockFile
.m_pObject
= NULL
;
294 SetUploadFileID(srcfile
);
296 // check extention to decide whether to compress or not
297 if (m_byDataCompVer
== 1 && GetFiletype(srcfile
->GetFileName()) != ftArchive
) {
298 CreatePackedPackets(area
.GetBuffer(), togo
, currentblock
);
300 CreateStandartPackets(area
.GetBuffer(), togo
, currentblock
);
304 srcfile
->statistic
.AddTransferred(togo
);
306 m_addedPayloadQueueSession
+= togo
;
308 Requested_Block_Struct
* block
= m_BlockRequests_queue
.front();
310 m_BlockRequests_queue
.pop_front();
311 m_DoneBlocks_list
.push_front(block
);
315 } catch (const wxString
& error
) {
316 AddDebugLogLineM(false, logClient
, wxT("Client '") + GetUserName() + wxT("' caused error while creating packet (") + error
+ wxT(") - disconnecting client"));
317 } catch (const CIOFailureException
& error
) {
318 AddDebugLogLineM(true, logClient
, wxT("IO failure while reading requested file: ") + error
.what());
319 } catch (const CEOFException
& WXUNUSED(error
)) {
320 AddDebugLogLineM(true, logClient
, GetClientFullInfo() + wxT(" requested file-data at an invalid position - disconnecting"));
324 theApp
->uploadqueue
->RemoveFromUploadQueue(this);
328 void CUpDownClient::CreateStandartPackets(const byte
* buffer
, uint32 togo
, Requested_Block_Struct
* currentblock
)
332 CMemFile
memfile(buffer
, togo
);
334 nPacketSize
= togo
/(uint32
)(togo
/10240);
340 if (togo
< nPacketSize
*2) {
344 wxASSERT(nPacketSize
);
347 uint64 endpos
= (currentblock
->EndOffset
- togo
);
348 uint64 startpos
= endpos
- nPacketSize
;
350 bool bLargeBlocks
= (startpos
> 0xFFFFFFFF) || (endpos
> 0xFFFFFFFF);
352 CMemFile
data(nPacketSize
+ 16 + 2 * (bLargeBlocks
? 8 :4));
353 data
.WriteHash(GetUploadFileID());
355 data
.WriteUInt64(startpos
);
356 data
.WriteUInt64(endpos
);
358 data
.WriteUInt32(startpos
);
359 data
.WriteUInt32(endpos
);
361 char *tempbuf
= new char[nPacketSize
];
362 memfile
.Read(tempbuf
, nPacketSize
);
363 data
.Write(tempbuf
, nPacketSize
);
365 CPacket
* packet
= new CPacket(data
, (bLargeBlocks
? OP_EMULEPROT
: OP_EDONKEYPROT
), (bLargeBlocks
? (uint8
)OP_SENDINGPART_I64
: (uint8
)OP_SENDINGPART
));
366 theStats::AddUpOverheadFileRequest(16 + 2 * (bLargeBlocks
? 8 :4));
367 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize
);
368 AddDebugLogLineM( false, logLocalClient
, wxString::Format(wxT("Local Client: %s to "),(bLargeBlocks
? wxT("OP_SENDINGPART_I64") : wxT("OP_SENDINGPART"))) + GetFullIP() );
369 m_socket
->SendPacket(packet
,true,false, nPacketSize
);
374 void CUpDownClient::CreatePackedPackets(const byte
* buffer
, uint32 togo
, Requested_Block_Struct
* currentblock
)
376 uLongf newsize
= togo
+300;
377 CScopedArray
<byte
> output(newsize
);
378 uint16 result
= compress2(output
.get(), &newsize
, buffer
, togo
, 9);
379 if (result
!= Z_OK
|| togo
<= newsize
){
380 CreateStandartPackets(buffer
, togo
, currentblock
);
384 CMemFile
memfile(output
.get(), newsize
);
386 uint32 totalPayloadSize
= 0;
387 uint32 oldSize
= togo
;
391 nPacketSize
= togo
/(uint32
)(togo
/10240);
397 if (togo
< nPacketSize
*2) {
402 bool isLargeBlock
= (currentblock
->StartOffset
> 0xFFFFFFFF) || (currentblock
->EndOffset
> 0xFFFFFFFF);
404 CMemFile
data(nPacketSize
+ 16 + (isLargeBlock
? 12 : 8));
405 data
.WriteHash(GetUploadFileID());
407 data
.WriteUInt64(currentblock
->StartOffset
);
409 data
.WriteUInt32(currentblock
->StartOffset
);
411 data
.WriteUInt32(newsize
);
412 char *tempbuf
= new char[nPacketSize
];
413 memfile
.Read(tempbuf
, nPacketSize
);
414 data
.Write(tempbuf
,nPacketSize
);
416 CPacket
* packet
= new CPacket(data
, OP_EMULEPROT
, (isLargeBlock
? OP_COMPRESSEDPART_I64
: OP_COMPRESSEDPART
));
418 // approximate payload size
419 uint32 payloadSize
= nPacketSize
*oldSize
/newsize
;
421 if (togo
== 0 && totalPayloadSize
+payloadSize
< oldSize
) {
422 payloadSize
= oldSize
-totalPayloadSize
;
425 totalPayloadSize
+= payloadSize
;
427 // put packet directly on socket
428 theStats::AddUpOverheadFileRequest(24);
429 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize
);
430 AddDebugLogLineM( false, logLocalClient
, wxString::Format(wxT("Local Client: %s to "), (isLargeBlock
? wxT("OP_COMPRESSEDPART_I64") : wxT("OP_COMPRESSEDPART"))) + GetFullIP() );
431 m_socket
->SendPacket(packet
,true,false, payloadSize
);
436 void CUpDownClient::ProcessExtendedInfo(const CMemFile
*data
, CKnownFile
*tempreqfile
)
438 m_uploadingfile
->UpdateUpPartsFrequency( this, false ); // Decrement
439 m_upPartStatus
.clear();
440 m_nUpCompleteSourcesCount
= 0;
442 if( GetExtendedRequestsVersion() == 0 ) {
443 // Something is coded wrong on this client if he's sending something it doesn't advertise.
447 if (data
->GetLength() == 16) {
448 // Wrong again. Advertised >0 but send a 0-type packet.
449 // But this time we'll disconnect it.
450 throw CInvalidPacket(wxT("Wrong size on extended info packet"));
453 uint16 nED2KUpPartCount
= data
->ReadUInt16();
454 if (!nED2KUpPartCount
) {
455 m_upPartStatus
.resize( tempreqfile
->GetPartCount(), 0 );
457 if (tempreqfile
->GetED2KPartCount() != nED2KUpPartCount
) {
458 // We already checked if we are talking about the same file.. So if we get here, something really strange happened!
459 m_upPartStatus
.clear();
463 m_upPartStatus
.resize( tempreqfile
->GetPartCount(), 0 );
467 while (done
!= m_upPartStatus
.size()) {
468 uint8 toread
= data
->ReadUInt8();
469 for (sint32 i
= 0;i
!= 8;i
++){
470 m_upPartStatus
[done
] = (toread
>>i
)&1;
471 // We may want to use this for another feature..
472 // if (m_upPartStatus[done] && !tempreqfile->IsComplete(done*PARTSIZE,((done+1)*PARTSIZE)-1))
473 // bPartsNeeded = true;
475 if (done
== m_upPartStatus
.size()) {
481 // We want the increment the frequency even if we didn't read everything
482 m_uploadingfile
->UpdateUpPartsFrequency( this, true ); // Increment
487 if (GetExtendedRequestsVersion() > 1) {
488 uint16 nCompleteCountLast
= GetUpCompleteSourcesCount();
489 uint16 nCompleteCountNew
= data
->ReadUInt16();
490 SetUpCompleteSourcesCount(nCompleteCountNew
);
491 if (nCompleteCountLast
!= nCompleteCountNew
) {
492 tempreqfile
->UpdatePartsInfo();
497 m_uploadingfile
->UpdateUpPartsFrequency( this, true ); // Increment
499 Notify_QlistRefreshClient(this);
503 void CUpDownClient::SetUploadFileID(CKnownFile
* newreqfile
)
505 if (m_uploadingfile
== newreqfile
) {
507 } else if (m_uploadingfile
) {
508 m_uploadingfile
->RemoveUploadingClient(this);
509 m_uploadingfile
->UpdateUpPartsFrequency(this, false); // Decrement
513 // This is a new file! update info
514 newreqfile
->AddUploadingClient(this);
516 if (m_requpfileid
!= newreqfile
->GetFileHash()) {
517 m_requpfileid
= newreqfile
->GetFileHash();
518 m_upPartStatus
.clear();
519 m_upPartStatus
.resize( newreqfile
->GetPartCount(), 0 );
521 // this is the same file we already had assigned. Only update data.
522 newreqfile
->UpdateUpPartsFrequency(this, true); // Increment
525 m_uploadingfile
= newreqfile
;
527 m_upPartStatus
.clear();
528 m_nUpCompleteSourcesCount
= 0;
529 // This clears m_uploadingfile and m_requpfileid
535 void CUpDownClient::AddReqBlock(Requested_Block_Struct
* reqblock
)
537 if (GetUploadState() != US_UPLOADING
) {
538 AddDebugLogLineM(false, logRemoteClient
, wxT("UploadClient: Client tried to add requested block when not in upload slot! Prevented requested blocks from being added."));
544 std::list
<Requested_Block_Struct
*>::iterator it
= m_DoneBlocks_list
.begin();
545 for (; it
!= m_DoneBlocks_list
.end(); ++it
) {
546 if (reqblock
->StartOffset
== (*it
)->StartOffset
&& reqblock
->EndOffset
== (*it
)->EndOffset
) {
554 std::list
<Requested_Block_Struct
*>::iterator it
= m_BlockRequests_queue
.begin();
555 for (; it
!= m_BlockRequests_queue
.end(); ++it
) {
556 if (reqblock
->StartOffset
== (*it
)->StartOffset
&& reqblock
->EndOffset
== (*it
)->EndOffset
) {
563 m_BlockRequests_queue
.push_back(reqblock
);
567 uint32
CUpDownClient::GetWaitStartTime() const
572 dwResult
= credits
->GetSecureWaitStartTime(GetIP());
574 if (dwResult
> m_dwUploadTime
&& IsDownloading()) {
575 // This happens only if two clients with invalid securehash are in the queue - if at all
576 dwResult
= m_dwUploadTime
- 1;
584 void CUpDownClient::SetWaitStartTime()
587 credits
->SetSecWaitStartTime(GetIP());
592 void CUpDownClient::ClearWaitStartTime()
595 credits
->ClearWaitStartTime();
600 uint32
CUpDownClient::SendBlockData()
602 uint32 curTick
= ::GetTickCount();
603 uint64 sentBytesCompleteFile
= 0;
604 uint64 sentBytesPartFile
= 0;
605 uint64 sentBytesPayload
= 0;
608 CEMSocket
* s
= m_socket
;
609 // uint32 uUpStatsPort = GetUserPort();
611 // Extended statistics information based on which client software and which port we sent this data to...
612 // This also updates the grand total for sent bytes, etc. And where this data came from.
613 sentBytesCompleteFile
= s
->GetSentBytesCompleteFileSinceLastCallAndReset();
614 sentBytesPartFile
= s
->GetSentBytesPartFileSinceLastCallAndReset();
615 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, false, true, sentBytesCompleteFile, (IsFriend() && GetFriendSlot()));
616 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, true, true, sentBytesPartFile, (IsFriend() && GetFriendSlot()));
618 m_nTransferredUp
+= sentBytesCompleteFile
+ sentBytesPartFile
;
619 credits
->AddUploaded(sentBytesCompleteFile
+ sentBytesPartFile
, GetIP(), theApp
->CryptoAvailable());
621 sentBytesPayload
= s
->GetSentPayloadSinceLastCallAndReset();
622 m_nCurQueueSessionPayloadUp
+= sentBytesPayload
;
624 if (theApp
->uploadqueue
->CheckForTimeOver(this)) {
625 theApp
->uploadqueue
->RemoveFromUploadQueue(this, true);
626 SendOutOfPartReqsAndAddToWaitingQueue();
628 // read blocks from file and put on socket
629 CreateNextBlockPackage();
633 if(sentBytesCompleteFile
+ sentBytesPartFile
> 0 ||
634 m_AvarageUDR_list
.empty() || (curTick
- m_AvarageUDR_list
.back().timestamp
) > 1*1000) {
635 // Store how much data we've transferred this round,
636 // to be able to calculate average speed later
637 // keep sum of all values in list up to date
638 TransferredData newitem
= {sentBytesCompleteFile
+ sentBytesPartFile
, curTick
};
639 m_AvarageUDR_list
.push_back(newitem
);
640 m_nSumForAvgUpDataRate
+= sentBytesCompleteFile
+ sentBytesPartFile
;
643 // remove to old values in list
644 while ((!m_AvarageUDR_list
.empty()) && (curTick
- m_AvarageUDR_list
.front().timestamp
) > 10*1000) {
645 // keep sum of all values in list up to date
646 m_nSumForAvgUpDataRate
-= m_AvarageUDR_list
.front().datalen
;
647 m_AvarageUDR_list
.pop_front();
650 // Calculate average speed for this slot
651 if ((!m_AvarageUDR_list
.empty()) && (curTick
- m_AvarageUDR_list
.front().timestamp
) > 0 && GetUpStartTimeDelay() > 2*1000) {
652 m_nUpDatarate
= ((uint64
)m_nSumForAvgUpDataRate
*1000) / (curTick
-m_AvarageUDR_list
.front().timestamp
);
654 // not enough values to calculate trustworthy speed. Use -1 to tell this
655 m_nUpDatarate
= 0; //-1;
658 // Check if it's time to update the display.
660 if (m_cSendblock
== 30){
662 Notify_UploadCtrlRefreshClient(this);
665 return sentBytesCompleteFile
+ sentBytesPartFile
;
669 void CUpDownClient::SendOutOfPartReqsAndAddToWaitingQueue()
671 // Kry - this is actually taken from eMule, but makes a lot of sense ;)
673 //OP_OUTOFPARTREQS will tell the downloading client to go back to OnQueue..
674 //The main reason for this is that if we put the client back on queue and it goes
675 //back to the upload before the socket times out... We get a situation where the
676 //downloader thinks it already sent the requested blocks and the uploader thinks
677 //the downloader didn't send any request blocks. Then the connection times out..
678 //I did some tests with eDonkey also and it seems to work well with them also..
680 // Send this inmediately, don't queue.
681 CPacket
* pPacket
= new CPacket(OP_OUTOFPARTREQS
, 0, OP_EDONKEYPROT
);
682 theStats::AddUpOverheadFileRequest(pPacket
->GetPacketSize());
683 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_OUTOFPARTREQS to ") + GetFullIP() );
684 SendPacket(pPacket
, true, true);
686 theApp
->uploadqueue
->AddClientToQueue(this);
691 * See description for CEMSocket::TruncateQueues().
693 void CUpDownClient::FlushSendBlocks()
695 // Call this when you stop upload, or the socket might be not able to send
696 if (m_socket
) { //socket may be NULL...
697 m_socket
->TruncateQueues();
702 void CUpDownClient::SendHashsetPacket(const CMD4Hash
& forfileid
)
704 CKnownFile
* file
= theApp
->sharedfiles
->GetFileByID( forfileid
);
705 bool from_dq
= false;
708 if ((file
= theApp
->downloadqueue
->GetFileByID(forfileid
)) == NULL
) {
709 AddLogLineM(false, CFormat( _("Hashset requested for unknown file: %s") ) % forfileid
.Encode() );
715 if ( !file
->GetHashCount() ) {
717 AddDebugLogLineM(false, logRemoteClient
, wxT("Requested hashset could not be found"));
720 file
= theApp
->downloadqueue
->GetFileByID(forfileid
);
721 if (!(file
&& file
->GetHashCount())) {
722 AddDebugLogLineM(false, logRemoteClient
, wxT("Requested hashset could not be found"));
729 data
.WriteHash(file
->GetFileHash());
730 uint16 parts
= file
->GetHashCount();
731 data
.WriteUInt16(parts
);
732 for (int i
= 0; i
!= parts
; i
++) {
733 data
.WriteHash(file
->GetPartHash(i
));
735 CPacket
* packet
= new CPacket(data
, OP_EDONKEYPROT
, OP_HASHSETANSWER
);
736 theStats::AddUpOverheadFileRequest(packet
->GetPacketSize());
737 AddDebugLogLineM( false, logLocalClient
, wxT("Local Client: OP_HASHSETANSWER to ") + GetFullIP());
738 SendPacket(packet
,true,true);
742 void CUpDownClient::ClearUploadBlockRequests()
745 DeleteContents(m_BlockRequests_queue
);
746 DeleteContents(m_DoneBlocks_list
);
750 void CUpDownClient::SendRankingInfo(){
751 if (!ExtProtocolAvailable()) {
755 uint16 nRank
= theApp
->uploadqueue
->GetWaitingPosition(this);
761 data
.WriteUInt16(nRank
);
762 // Kry: what are these zero bytes for. are they really correct?
763 // Kry - Well, eMule does like that. I guess they're ok.
764 data
.WriteUInt32(0); data
.WriteUInt32(0); data
.WriteUInt16(0);
765 CPacket
* packet
= new CPacket(data
, OP_EMULEPROT
, OP_QUEUERANKING
);
766 theStats::AddUpOverheadOther(packet
->GetPacketSize());
767 AddDebugLogLineM(false, logLocalClient
, wxT("Local Client: OP_QUEUERANKING to ") + GetFullIP());
768 SendPacket(packet
,true,true);
772 void CUpDownClient::SendCommentInfo(CKnownFile
* file
)
774 if (!m_bCommentDirty
|| file
== NULL
|| !ExtProtocolAvailable() || m_byAcceptCommentVer
< 1) {
777 m_bCommentDirty
= false;
779 // Truncate to max len.
780 wxString desc
= file
->GetFileComment().Left(MAXFILECOMMENTLEN
);
781 uint8 rating
= file
->GetFileRating();
783 if ( file
->GetFileRating() == 0 && desc
.IsEmpty() ) {
788 data
.WriteUInt8(rating
);
789 data
.WriteString(desc
, GetUnicodeSupport(), 4 /* size it's uint32 */);
791 CPacket
* packet
= new CPacket(data
, OP_EMULEPROT
, OP_FILEDESC
);
792 theStats::AddUpOverheadOther(packet
->GetPacketSize());
793 AddDebugLogLineM(false, logLocalClient
, wxT("Local Client: OP_FILEDESC to ") + GetFullIP());
794 SendPacket(packet
,true);
797 void CUpDownClient::UnBan(){
798 m_Aggressiveness
= 0;
800 theApp
->clientlist
->AddTrackClient(this);
801 theApp
->clientlist
->RemoveBannedClient( GetIP() );
802 SetUploadState(US_NONE
);
803 ClearWaitStartTime();
805 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
808 void CUpDownClient::Ban(){
809 theApp
->clientlist
->AddTrackClient(this);
810 theApp
->clientlist
->AddBannedClient( GetIP() );
812 AddDebugLogLineM( false, logClient
, wxT("Client '") + GetUserName() + wxT("' seems to be an aggressive client and is banned from the uploadqueue"));
814 SetUploadState(US_BANNED
);
816 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
817 Notify_QlistRefreshClient(this);
820 bool CUpDownClient::IsBanned() const
822 return ( (theApp
->clientlist
->IsBannedClient(GetIP()) ) && m_nDownloadState
!= DS_DOWNLOADING
);
825 void CUpDownClient::CheckForAggressive()
827 uint32 cur_time
= ::GetTickCount();
829 // First call, initalize
830 if ( !m_LastFileRequest
) {
831 m_LastFileRequest
= cur_time
;
835 // Is this an aggressive request?
836 if ( ( cur_time
- m_LastFileRequest
) < MIN_REQUESTTIME
) {
837 m_Aggressiveness
+= 3;
839 // Is the client EVIL?
840 if ( m_Aggressiveness
>= 10 && (!IsBanned() && m_nDownloadState
!= DS_DOWNLOADING
)) {
841 AddDebugLogLineM( false, logClient
, CFormat( wxT("Aggressive client banned (score: %d): %s -- %s -- %s") )
845 % m_fullClientVerString
);
849 // Polite request, reward client
850 if ( m_Aggressiveness
)
854 m_LastFileRequest
= cur_time
;
858 void CUpDownClient::SetUploadFileID(const CMD4Hash
& new_id
)
860 // Update the uploading file found
861 CKnownFile
* uploadingfile
= theApp
->sharedfiles
->GetFileByID(new_id
);
862 if ( !uploadingfile
) {
863 // Can this really happen?
864 uploadingfile
= theApp
->downloadqueue
->GetFileByID(new_id
);
866 SetUploadFileID(uploadingfile
); // This will update queue count on old and new file.
869 void CUpDownClient::ProcessRequestPartsPacket(const byte
* pachPacket
, uint32 nSize
, bool largeblocks
) {
871 CMemFile
data(pachPacket
, nSize
);
873 CMD4Hash reqfilehash
= data
.ReadHash();
875 uint64 auStartOffsets
[3];
876 uint64 auEndOffsets
[3];
879 auStartOffsets
[0] = data
.ReadUInt64();
880 auStartOffsets
[1] = data
.ReadUInt64();
881 auStartOffsets
[2] = data
.ReadUInt64();
883 auEndOffsets
[0] = data
.ReadUInt64();
884 auEndOffsets
[1] = data
.ReadUInt64();
885 auEndOffsets
[2] = data
.ReadUInt64();
887 auStartOffsets
[0] = data
.ReadUInt32();
888 auStartOffsets
[1] = data
.ReadUInt32();
889 auStartOffsets
[2] = data
.ReadUInt32();
891 auEndOffsets
[0] = data
.ReadUInt32();
892 auEndOffsets
[1] = data
.ReadUInt32();
893 auEndOffsets
[2] = data
.ReadUInt32();
896 for (unsigned int i
= 0; i
< itemsof(auStartOffsets
); i
++) {
897 AddDebugLogLineM(false, logClient
,
898 wxString::Format(wxT("Client requests %u"), i
)
899 + wxT(" ") + wxString::Format(wxT("File block %u-%u (%d bytes):"), auStartOffsets
[i
], auEndOffsets
[i
], auEndOffsets
[i
] - auStartOffsets
[i
])
900 + wxT(" ") + GetFullIP());
901 if (auEndOffsets
[i
] > auStartOffsets
[i
]) {
902 Requested_Block_Struct
* reqblock
= new Requested_Block_Struct
;
903 reqblock
->StartOffset
= auStartOffsets
[i
];
904 reqblock
->EndOffset
= auEndOffsets
[i
];
905 md4cpy(reqblock
->FileID
, reqfilehash
.GetHash());
906 reqblock
->transferred
= 0;
907 AddReqBlock(reqblock
);
909 if (auEndOffsets
[i
] != 0 || auStartOffsets
[i
] != 0) {
910 AddDebugLogLineM(false, logClient
, wxT("Client request is invalid!"));
916 void CUpDownClient::ProcessRequestPartsPacketv2(const CMemFile
& data
) {
918 CMD4Hash reqfilehash
= data
.ReadHash();
920 uint8 numblocks
= data
.ReadUInt8();
922 for (int i
= 0; i
< numblocks
; i
++) {
923 Requested_Block_Struct
* reqblock
= new Requested_Block_Struct
;
925 reqblock
->StartOffset
= data
.GetIntTagValue();
926 // We have to do +1, because the block matching uses that.
927 reqblock
->EndOffset
= data
.GetIntTagValue() + 1;
928 if ((reqblock
->StartOffset
|| reqblock
->EndOffset
) && (reqblock
->StartOffset
> reqblock
->EndOffset
)) {
929 AddDebugLogLineM(false, logClient
, wxString::Format(wxT("Client request is invalid! %i / %i"),reqblock
->StartOffset
, reqblock
->EndOffset
));
930 throw wxString(wxT("Client request is invalid!"));
933 AddDebugLogLineM(false, logClient
,
934 wxString::Format(wxT("Client requests %u"), i
)
935 + wxT(" ") + wxString::Format(wxT("File block %u-%u (%d bytes):"),reqblock
->StartOffset
, reqblock
->EndOffset
, reqblock
->EndOffset
- reqblock
->StartOffset
)
936 += wxT(" ") + GetFullIP());
938 md4cpy(reqblock
->FileID
, reqfilehash
.GetHash());
939 reqblock
->transferred
= 0;
940 AddReqBlock(reqblock
);
947 // File_checked_for_headers