No reason to drop requests of already downloading clients if the upload queue is...
[amule.git] / src / UploadClient.cpp
blob73b8dac76a6d821f75c92f47a28e0cb703d5cfc8
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "updownclient.h" // Interface
28 #include <protocol/Protocols.h>
29 #include <protocol/ed2k/Client2Client/TCP.h>
31 #include <zlib.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
44 #include "Logger.h"
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
58 m_nUpDatarate = 0;
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);
72 uint32 CUpDownClient::GetScore( bool sysvalue ) const // true: return zero for lowID client (unless it's connected)
74 //TODO: complete this (friends, uploadspeed, amuleuser etc etc)
75 if (m_Username.IsEmpty()) {
76 return 0;
79 if (credits == 0) {
80 return 0;
83 const CKnownFile* pFile = GetUploadFile();
84 if ( !pFile ) {
85 return 0;
88 // bad clients (see note in function)
89 if (IsBadGuy()) {
90 return 0;
93 // friend slot
94 if (IsFriend() && GetFriendSlot() && !HasLowID()) {
95 return 0x0FFFFFFF;
98 if (IsBanned())
99 return 0;
101 if (sysvalue && HasLowID() && !IsConnected()){
102 return 0;
105 // score applies only to waiting clients, not to downloading clients
106 if (IsDownloading()) {
107 return 0;
110 // calculate score, based on waitingtime and other factors
111 float fBaseValue = (float)(::GetTickCount()-GetWaitStartTime())/1000;
113 fBaseValue *= GetScoreRatio(); // credits
115 // Take file upload priority into account
117 // One yet unsolved problem here:
118 // sometimes a client asks for 2 files and there is no way to decide, which file the
119 // client finally gets. so it could happen that he is queued first because of a
120 // high prio file, but then asks for something completely different.
121 float filepriority;
122 switch (pFile->GetUpPriority()) {
123 case PR_POWERSHARE:
124 filepriority = 250.0f;
125 break;
126 case PR_VERYHIGH:
127 filepriority = 1.8f;
128 break;
129 case PR_HIGH:
130 filepriority = 0.9f;
131 break;
132 case PR_LOW:
133 filepriority = 0.6f;
134 break;
135 case PR_VERYLOW:
136 filepriority = 0.2f;
137 break;
138 case PR_NORMAL:
139 default:
140 filepriority = 0.7f;
141 break;
143 fBaseValue *= filepriority;
145 if ( (IsEmuleClient() || GetClientSoft() < 10) && m_byEmuleVersion <= 0x19) {
146 fBaseValue *= 0.5f;
148 return (uint32)fBaseValue;
152 // Checks if it is next requested block from another chunk of the actual file or from another file
154 // [Returns]
155 // true : Next requested block is from another different chunk or file than last downloaded block
156 // false: Next requested block is from same chunk that last downloaded block
157 bool CUpDownClient::IsDifferentPartBlock() const // [Tarod 12/22/2002]
159 bool different_part = false;
161 // Check if we have good lists and proceed to check for different chunks
162 if ((!m_BlockRequests_queue.empty()) && !m_DoneBlocks_list.empty())
164 Requested_Block_Struct* last_done_block = NULL;
165 Requested_Block_Struct* next_requested_block = NULL;
166 uint64 last_done_part = 0xffffffff;
167 uint64 next_requested_part = 0xffffffff;
170 // Get last block and next pending
171 last_done_block = m_DoneBlocks_list.front();
172 next_requested_block = m_BlockRequests_queue.front();
174 // Calculate corresponding parts to blocks
175 last_done_part = last_done_block->StartOffset / PARTSIZE;
176 next_requested_part = next_requested_block->StartOffset / PARTSIZE;
178 // Test is we are asking same file and same part
179 if ( last_done_part != next_requested_part) {
180 different_part = true;
181 AddDebugLogLineN(logClient, wxT("Session ended due to new chunk."));
184 if (md4cmp(last_done_block->FileID, next_requested_block->FileID) != 0) {
185 different_part = true;
186 AddDebugLogLineN(logClient, wxT("Session ended due to different file."));
190 return different_part;
194 void CUpDownClient::CreateNextBlockPackage()
196 try {
197 // Buffer new data if current buffer is less than 100 KBytes
198 while (!m_BlockRequests_queue.empty()
199 && m_addedPayloadQueueSession - m_nCurQueueSessionPayloadUp < 100*1024) {
201 Requested_Block_Struct* currentblock = m_BlockRequests_queue.front();
202 CKnownFile* srcfile = theApp->sharedfiles->GetFileByID(CMD4Hash(currentblock->FileID));
204 if (!srcfile) {
205 throw wxString(wxT("requested file not found"));
208 // Check if this know file is a CPartFile.
209 // For completed part files IsPartFile() returns false, so they are
210 // correctly treated as plain CKnownFile.
211 CPartFile* srcPartFile = srcfile->IsPartFile() ? (CPartFile*)srcfile : NULL;
213 // THIS EndOffset points BEHIND the last byte requested
214 // (other than the offsets used in the PartFile code)
215 if (currentblock->EndOffset > srcfile->GetFileSize()) {
216 throw wxString(CFormat(wxT("Asked for data up to %d beyond end of file (%d)"))
217 % currentblock->EndOffset % srcfile->GetFileSize());
218 } else if (currentblock->StartOffset > currentblock->EndOffset) {
219 throw wxString(CFormat(wxT("Asked for invalid block (start %d > end %d)"))
220 % currentblock->StartOffset % currentblock->EndOffset);
223 uint64 togo = currentblock->EndOffset - currentblock->StartOffset;
225 if (togo > EMBLOCKSIZE * 3) {
226 throw wxString(CFormat(wxT("Client requested too large block (%d > %d)"))
227 % togo % (EMBLOCKSIZE * 3));
230 CFileArea area;
231 if (srcPartFile) {
232 if (!srcPartFile->IsComplete(currentblock->StartOffset,currentblock->EndOffset-1)) {
233 throw wxString(CFormat(wxT("Asked for incomplete block (%d - %d)"))
234 % currentblock->StartOffset % (currentblock->EndOffset-1));
236 if (!srcPartFile->ReadData(area, currentblock->StartOffset, togo)) {
237 throw wxString(wxT("Failed to read from requested partfile"));
239 } else {
240 CFileAutoClose file;
241 CPath fullname = srcfile->GetFilePath().JoinPaths(srcfile->GetFileName());
242 if ( !file.Open(fullname, CFile::read) ) {
243 // The file was most likely moved/deleted. So remove it from the list of shared files.
244 AddLogLineN(CFormat( _("Failed to open file (%s), removing from list of shared files.") ) % srcfile->GetFileName() );
245 theApp->sharedfiles->RemoveFile(srcfile);
247 throw wxString(wxT("Failed to open requested file: Removing from list of shared files!"));
249 area.ReadAt(file, currentblock->StartOffset, togo);
251 area.CheckError();
253 SetUploadFileID(srcfile);
255 // check extention to decide whether to compress or not
256 if (m_byDataCompVer == 1 && GetFiletype(srcfile->GetFileName()) != ftArchive) {
257 CreatePackedPackets(area.GetBuffer(), togo, currentblock);
258 } else {
259 CreateStandardPackets(area.GetBuffer(), togo, currentblock);
262 // file statistic
263 srcfile->statistic.AddTransferred(togo);
265 m_addedPayloadQueueSession += togo;
267 Requested_Block_Struct* block = m_BlockRequests_queue.front();
269 m_BlockRequests_queue.pop_front();
270 m_DoneBlocks_list.push_front(block);
273 return;
274 } catch (const wxString& error) {
275 AddDebugLogLineN(logClient,
276 CFormat(wxT("Client '%s' (%s) caused error while creating packet (%s) - disconnecting client"))
277 % GetUserName() % GetFullIP() % error);
278 } catch (const CIOFailureException& error) {
279 AddDebugLogLineC(logClient, wxT("IO failure while reading requested file: ") + error.what());
280 } catch (const CEOFException& WXUNUSED(error)) {
281 AddDebugLogLineC(logClient, GetClientFullInfo() + wxT(" requested file-data at an invalid position - disconnecting"));
284 // Error occured.
285 theApp->uploadqueue->RemoveFromUploadQueue(this);
289 void CUpDownClient::CreateStandardPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock)
291 uint32 nPacketSize;
293 CMemFile memfile(buffer, togo);
294 if (togo > 10240) {
295 nPacketSize = togo/(uint32)(togo/10240);
296 } else {
297 nPacketSize = togo;
300 while (togo){
301 if (togo < nPacketSize*2) {
302 nPacketSize = togo;
305 wxASSERT(nPacketSize);
306 togo -= nPacketSize;
308 uint64 endpos = (currentblock->EndOffset - togo);
309 uint64 startpos = endpos - nPacketSize;
311 bool bLargeBlocks = (startpos > 0xFFFFFFFF) || (endpos > 0xFFFFFFFF);
313 CMemFile data(nPacketSize + 16 + 2 * (bLargeBlocks ? 8 :4));
314 data.WriteHash(GetUploadFileID());
315 if (bLargeBlocks) {
316 data.WriteUInt64(startpos);
317 data.WriteUInt64(endpos);
318 } else {
319 data.WriteUInt32(startpos);
320 data.WriteUInt32(endpos);
322 char *tempbuf = new char[nPacketSize];
323 memfile.Read(tempbuf, nPacketSize);
324 data.Write(tempbuf, nPacketSize);
325 delete [] tempbuf;
326 CPacket* packet = new CPacket(data, (bLargeBlocks ? OP_EMULEPROT : OP_EDONKEYPROT), (bLargeBlocks ? (uint8)OP_SENDINGPART_I64 : (uint8)OP_SENDINGPART));
327 theStats::AddUpOverheadFileRequest(16 + 2 * (bLargeBlocks ? 8 :4));
328 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize);
329 AddDebugLogLineN(logLocalClient,
330 CFormat(wxT("Local Client: %s to %s"))
331 % (bLargeBlocks ? wxT("OP_SENDINGPART_I64") : wxT("OP_SENDINGPART")) % GetFullIP() );
332 m_socket->SendPacket(packet,true,false, nPacketSize);
337 void CUpDownClient::CreatePackedPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock)
339 uLongf newsize = togo+300;
340 CScopedArray<byte> output(newsize);
341 uint16 result = compress2(output.get(), &newsize, buffer, togo, 9);
342 if (result != Z_OK || togo <= newsize){
343 CreateStandardPackets(buffer, togo, currentblock);
344 return;
347 CMemFile memfile(output.get(), newsize);
349 uint32 totalPayloadSize = 0;
350 uint32 oldSize = togo;
351 togo = newsize;
352 uint32 nPacketSize;
353 if (togo > 10240) {
354 nPacketSize = togo/(uint32)(togo/10240);
355 } else {
356 nPacketSize = togo;
359 while (togo) {
360 if (togo < nPacketSize*2) {
361 nPacketSize = togo;
363 togo -= nPacketSize;
365 bool isLargeBlock = (currentblock->StartOffset > 0xFFFFFFFF) || (currentblock->EndOffset > 0xFFFFFFFF);
367 CMemFile data(nPacketSize + 16 + (isLargeBlock ? 12 : 8));
368 data.WriteHash(GetUploadFileID());
369 if (isLargeBlock) {
370 data.WriteUInt64(currentblock->StartOffset);
371 } else {
372 data.WriteUInt32(currentblock->StartOffset);
374 data.WriteUInt32(newsize);
375 char *tempbuf = new char[nPacketSize];
376 memfile.Read(tempbuf, nPacketSize);
377 data.Write(tempbuf,nPacketSize);
378 delete [] tempbuf;
379 CPacket* packet = new CPacket(data, OP_EMULEPROT, (isLargeBlock ? OP_COMPRESSEDPART_I64 : OP_COMPRESSEDPART));
381 // approximate payload size
382 uint32 payloadSize = nPacketSize*oldSize/newsize;
384 if (togo == 0 && totalPayloadSize+payloadSize < oldSize) {
385 payloadSize = oldSize-totalPayloadSize;
388 totalPayloadSize += payloadSize;
390 // put packet directly on socket
391 theStats::AddUpOverheadFileRequest(24);
392 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize);
393 AddDebugLogLineN(logLocalClient,
394 CFormat(wxT("Local Client: %s to %s"))
395 % (isLargeBlock ? wxT("OP_COMPRESSEDPART_I64") : wxT("OP_COMPRESSEDPART")) % GetFullIP() );
396 m_socket->SendPacket(packet,true,false, payloadSize);
401 void CUpDownClient::ProcessExtendedInfo(const CMemFile *data, CKnownFile *tempreqfile)
403 m_uploadingfile->UpdateUpPartsFrequency( this, false ); // Decrement
404 m_upPartStatus.clear();
405 m_nUpCompleteSourcesCount= 0;
407 if( GetExtendedRequestsVersion() == 0 ) {
408 // Something is coded wrong on this client if he's sending something it doesn't advertise.
409 return;
412 if (data->GetLength() == 16) {
413 // Wrong again. Advertised >0 but send a 0-type packet.
414 // But this time we'll disconnect it.
415 throw CInvalidPacket(wxT("Wrong size on extended info packet"));
418 uint16 nED2KUpPartCount = data->ReadUInt16();
419 if (!nED2KUpPartCount) {
420 m_upPartStatus.setsize( tempreqfile->GetPartCount(), 0 );
421 } else {
422 if (tempreqfile->GetED2KPartCount() != nED2KUpPartCount) {
423 // We already checked if we are talking about the same file.. So if we get here, something really strange happened!
424 m_upPartStatus.clear();
425 return;
428 m_upPartStatus.setsize( tempreqfile->GetPartCount(), 0 );
430 try {
431 uint16 done = 0;
432 while (done != m_upPartStatus.size()) {
433 uint8 toread = data->ReadUInt8();
434 for (sint32 i = 0;i != 8;i++){
435 m_upPartStatus.set(done, (toread>>i)&1);
436 // We may want to use this for another feature..
437 // if (m_upPartStatus[done] && !tempreqfile->IsComplete(done*PARTSIZE,((done+1)*PARTSIZE)-1))
438 // bPartsNeeded = true;
439 done++;
440 if (done == m_upPartStatus.size()) {
441 break;
445 } catch (...) {
446 // We want the increment the frequency even if we didn't read everything
447 m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment
449 throw;
452 if (GetExtendedRequestsVersion() > 1) {
453 uint16 nCompleteCountLast = GetUpCompleteSourcesCount();
454 uint16 nCompleteCountNew = data->ReadUInt16();
455 SetUpCompleteSourcesCount(nCompleteCountNew);
456 if (nCompleteCountLast != nCompleteCountNew) {
457 tempreqfile->UpdatePartsInfo();
462 m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment
464 Notify_SharedCtrlRefreshClient(this, AVAILABLE_SOURCE);
468 void CUpDownClient::SetUploadFileID(CKnownFile* newreqfile)
470 if (m_uploadingfile == newreqfile) {
471 return;
472 } else if (m_uploadingfile) {
473 m_uploadingfile->RemoveUploadingClient(this);
474 m_uploadingfile->UpdateUpPartsFrequency(this, false); // Decrement
477 if (newreqfile) {
478 // This is a new file! update info
479 newreqfile->AddUploadingClient(this);
481 if (m_requpfileid != newreqfile->GetFileHash()) {
482 m_requpfileid = newreqfile->GetFileHash();
483 m_upPartStatus.setsize( newreqfile->GetPartCount(), 0 );
484 } else {
485 // this is the same file we already had assigned. Only update data.
486 newreqfile->UpdateUpPartsFrequency(this, true); // Increment
489 m_uploadingfile = newreqfile;
490 } else {
491 m_upPartStatus.clear();
492 m_nUpCompleteSourcesCount = 0;
493 // This clears m_uploadingfile and m_requpfileid
494 ClearUploadFileID();
499 void CUpDownClient::AddReqBlock(Requested_Block_Struct* reqblock)
501 if (GetUploadState() != US_UPLOADING) {
502 AddDebugLogLineN(logRemoteClient, wxT("UploadClient: Client tried to add requested block when not in upload slot! Prevented requested blocks from being added."));
503 delete reqblock;
504 return;
508 std::list<Requested_Block_Struct*>::iterator it = m_DoneBlocks_list.begin();
509 for (; it != m_DoneBlocks_list.end(); ++it) {
510 if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) {
511 delete reqblock;
512 return;
518 std::list<Requested_Block_Struct*>::iterator it = m_BlockRequests_queue.begin();
519 for (; it != m_BlockRequests_queue.end(); ++it) {
520 if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) {
521 delete reqblock;
522 return;
527 m_BlockRequests_queue.push_back(reqblock);
531 uint32 CUpDownClient::GetWaitStartTime() const
533 uint32 dwResult = 0;
535 if ( credits ) {
536 dwResult = credits->GetSecureWaitStartTime(GetIP());
538 if (dwResult > m_dwUploadTime && IsDownloading()) {
539 // This happens only if two clients with invalid securehash are in the queue - if at all
540 dwResult = m_dwUploadTime - 1;
544 return dwResult;
548 void CUpDownClient::SetWaitStartTime()
550 if ( credits ) {
551 credits->SetSecWaitStartTime(GetIP());
556 void CUpDownClient::ClearWaitStartTime()
558 if ( credits ) {
559 credits->ClearWaitStartTime();
564 void CUpDownClient::ResetSessionUp()
566 m_nCurSessionUp = m_nTransferredUp;
567 m_addedPayloadQueueSession = 0;
568 m_nCurQueueSessionPayloadUp = 0;
569 // If upload was resumed there can be a remaining payload in the socket
570 // causing (prepared - sent) getting negative. So reset the counter here.
571 if (m_socket) {
572 CEMSocket* s = m_socket;
573 s->GetSentPayloadSinceLastCallAndReset();
578 uint32 CUpDownClient::SendBlockData()
580 uint32 curTick = ::GetTickCount();
581 uint64 sentBytesCompleteFile = 0;
582 uint64 sentBytesPartFile = 0;
583 uint64 sentBytesPayload = 0;
585 if (m_socket) {
586 CEMSocket* s = m_socket;
587 // uint32 uUpStatsPort = GetUserPort();
589 // Extended statistics information based on which client software and which port we sent this data to...
590 // This also updates the grand total for sent bytes, etc. And where this data came from.
591 sentBytesCompleteFile = s->GetSentBytesCompleteFileSinceLastCallAndReset();
592 sentBytesPartFile = s->GetSentBytesPartFileSinceLastCallAndReset();
593 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, false, true, sentBytesCompleteFile, (IsFriend() && GetFriendSlot()));
594 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, true, true, sentBytesPartFile, (IsFriend() && GetFriendSlot()));
596 m_nTransferredUp += sentBytesCompleteFile + sentBytesPartFile;
597 credits->AddUploaded(sentBytesCompleteFile + sentBytesPartFile, GetIP(), theApp->CryptoAvailable());
599 sentBytesPayload = s->GetSentPayloadSinceLastCallAndReset();
600 m_nCurQueueSessionPayloadUp += sentBytesPayload;
602 if (theApp->uploadqueue->CheckForTimeOver(this)) {
603 theApp->uploadqueue->RemoveFromUploadQueue(this);
604 SendOutOfPartReqsAndAddToWaitingQueue();
605 } else {
606 // read blocks from file and put on socket
607 CreateNextBlockPackage();
611 if(sentBytesCompleteFile + sentBytesPartFile > 0 ||
612 m_AvarageUDR_list.empty() || (curTick - m_AvarageUDR_list.back().timestamp) > 1*1000) {
613 // Store how much data we've transferred this round,
614 // to be able to calculate average speed later
615 // keep sum of all values in list up to date
616 TransferredData newitem = {sentBytesCompleteFile + sentBytesPartFile, curTick};
617 m_AvarageUDR_list.push_back(newitem);
618 m_nSumForAvgUpDataRate += sentBytesCompleteFile + sentBytesPartFile;
621 // remove to old values in list
622 while ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 10*1000) {
623 // keep sum of all values in list up to date
624 m_nSumForAvgUpDataRate -= m_AvarageUDR_list.front().datalen;
625 m_AvarageUDR_list.pop_front();
628 // Calculate average speed for this slot
629 if ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 0 && GetUpStartTimeDelay() > 2*1000) {
630 m_nUpDatarate = ((uint64)m_nSumForAvgUpDataRate*1000) / (curTick-m_AvarageUDR_list.front().timestamp);
631 } else {
632 // not enough values to calculate trustworthy speed. Use -1 to tell this
633 m_nUpDatarate = 0; //-1;
636 // Check if it's time to update the display.
637 m_cSendblock++;
638 if (m_cSendblock == 30){
639 m_cSendblock = 0;
640 Notify_SharedCtrlRefreshClient(this, AVAILABLE_SOURCE);
643 return sentBytesCompleteFile + sentBytesPartFile;
647 void CUpDownClient::SendOutOfPartReqsAndAddToWaitingQueue()
649 // Kry - this is actually taken from eMule, but makes a lot of sense ;)
651 //OP_OUTOFPARTREQS will tell the downloading client to go back to OnQueue..
652 //The main reason for this is that if we put the client back on queue and it goes
653 //back to the upload before the socket times out... We get a situation where the
654 //downloader thinks it already sent the requested blocks and the uploader thinks
655 //the downloader didn't send any request blocks. Then the connection times out..
656 //I did some tests with eDonkey also and it seems to work well with them also..
658 // Send this inmediately, don't queue.
659 CPacket* pPacket = new CPacket(OP_OUTOFPARTREQS, 0, OP_EDONKEYPROT);
660 theStats::AddUpOverheadFileRequest(pPacket->GetPacketSize());
661 AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_OUTOFPARTREQS to ") + GetFullIP() );
662 SendPacket(pPacket, true, true);
664 theApp->uploadqueue->AddClientToQueue(this);
669 * See description for CEMSocket::TruncateQueues().
671 void CUpDownClient::FlushSendBlocks()
673 // Call this when you stop upload, or the socket might be not able to send
674 if (m_socket) { //socket may be NULL...
675 m_socket->TruncateQueues();
680 void CUpDownClient::SendHashsetPacket(const CMD4Hash& forfileid)
682 CKnownFile* file = theApp->sharedfiles->GetFileByID( forfileid );
683 bool from_dq = false;
684 if ( !file ) {
685 from_dq = true;
686 if ((file = theApp->downloadqueue->GetFileByID(forfileid)) == NULL) {
687 AddLogLineN(CFormat( _("Hashset requested for unknown file: %s") ) % forfileid.Encode() );
689 return;
693 if ( !file->GetHashCount() ) {
694 if (from_dq) {
695 AddDebugLogLineN(logRemoteClient, wxT("Requested hashset could not be found"));
696 return;
697 } else {
698 file = theApp->downloadqueue->GetFileByID(forfileid);
699 if (!(file && file->GetHashCount())) {
700 AddDebugLogLineN(logRemoteClient, wxT("Requested hashset could not be found"));
701 return;
706 CMemFile data(1024);
707 data.WriteHash(file->GetFileHash());
708 uint16 parts = file->GetHashCount();
709 data.WriteUInt16(parts);
710 for (int i = 0; i != parts; i++) {
711 data.WriteHash(file->GetPartHash(i));
713 CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_HASHSETANSWER);
714 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
715 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_HASHSETANSWER to ") + GetFullIP());
716 SendPacket(packet,true,true);
720 void CUpDownClient::ClearUploadBlockRequests()
722 FlushSendBlocks();
723 DeleteContents(m_BlockRequests_queue);
724 DeleteContents(m_DoneBlocks_list);
727 uint16 CUpDownClient::GetUploadQueueWaitingPosition() const
729 return theApp->uploadqueue->GetWaitingPosition(this);
732 void CUpDownClient::SendRankingInfo(){
733 if (!ExtProtocolAvailable()) {
734 return;
737 uint16 nRank = GetUploadQueueWaitingPosition();
738 if (!nRank) {
739 return;
742 CMemFile data;
743 data.WriteUInt16(nRank);
744 // Kry: what are these zero bytes for. are they really correct?
745 // Kry - Well, eMule does like that. I guess they're ok.
746 data.WriteUInt32(0); data.WriteUInt32(0); data.WriteUInt16(0);
747 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_QUEUERANKING);
748 theStats::AddUpOverheadOther(packet->GetPacketSize());
749 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_QUEUERANKING to ") + GetFullIP());
750 SendPacket(packet,true,true);
754 void CUpDownClient::SendCommentInfo(CKnownFile* file)
756 if (!m_bCommentDirty || file == NULL || !ExtProtocolAvailable() || m_byAcceptCommentVer < 1) {
757 return;
759 m_bCommentDirty = false;
761 // Truncate to max len.
762 wxString desc = file->GetFileComment().Left(MAXFILECOMMENTLEN);
763 uint8 rating = file->GetFileRating();
765 if ( file->GetFileRating() == 0 && desc.IsEmpty() ) {
766 return;
769 CMemFile data(256);
770 data.WriteUInt8(rating);
771 data.WriteString(desc, GetUnicodeSupport(), 4 /* size it's uint32 */);
773 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_FILEDESC);
774 theStats::AddUpOverheadOther(packet->GetPacketSize());
775 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_FILEDESC to ") + GetFullIP());
776 SendPacket(packet,true);
779 void CUpDownClient::UnBan(){
780 m_Aggressiveness = 0;
782 theApp->clientlist->AddTrackClient(this);
783 theApp->clientlist->RemoveBannedClient( GetIP() );
784 SetUploadState(US_NONE);
785 ClearWaitStartTime();
787 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
790 void CUpDownClient::Ban(){
791 theApp->clientlist->AddTrackClient(this);
792 theApp->clientlist->AddBannedClient( GetIP() );
794 AddDebugLogLineN(logClient, wxT("Client '") + GetUserName() + wxT("' seems to be an aggressive client and is banned from the uploadqueue"));
796 SetUploadState(US_BANNED);
798 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
799 Notify_SharedCtrlRefreshClient(this, UNAVAILABLE_SOURCE);
802 bool CUpDownClient::IsBanned() const
804 return ( (theApp->clientlist->IsBannedClient(GetIP()) ) && m_nDownloadState != DS_DOWNLOADING);
807 void CUpDownClient::CheckForAggressive()
809 uint32 cur_time = ::GetTickCount();
811 // First call, initalize
812 if ( !m_LastFileRequest ) {
813 m_LastFileRequest = cur_time;
814 return;
817 // Is this an aggressive request?
818 if ( ( cur_time - m_LastFileRequest ) < MIN_REQUESTTIME ) {
819 m_Aggressiveness += 3;
821 // Is the client EVIL?
822 if ( m_Aggressiveness >= 10 && (!IsBanned() && m_nDownloadState != DS_DOWNLOADING )) {
823 AddDebugLogLineN(logClient, CFormat( wxT("Aggressive client banned (score: %d): %s -- %s -- %s") )
824 % m_Aggressiveness
825 % m_Username
826 % m_strModVersion
827 % m_fullClientVerString );
828 Ban();
830 } else {
831 // Polite request, reward client
832 if ( m_Aggressiveness )
833 m_Aggressiveness--;
836 m_LastFileRequest = cur_time;
840 void CUpDownClient::SetUploadFileID(const CMD4Hash& new_id)
842 // Update the uploading file found
843 CKnownFile* uploadingfile = theApp->sharedfiles->GetFileByID(new_id);
844 if ( !uploadingfile ) {
845 // Can this really happen?
846 uploadingfile = theApp->downloadqueue->GetFileByID(new_id);
848 SetUploadFileID(uploadingfile); // This will update queue count on old and new file.
851 void CUpDownClient::ProcessRequestPartsPacket(const byte* pachPacket, uint32 nSize, bool largeblocks) {
853 CMemFile data(pachPacket, nSize);
855 CMD4Hash reqfilehash = data.ReadHash();
857 uint64 auStartOffsets[3];
858 uint64 auEndOffsets[3];
860 if (largeblocks) {
861 auStartOffsets[0] = data.ReadUInt64();
862 auStartOffsets[1] = data.ReadUInt64();
863 auStartOffsets[2] = data.ReadUInt64();
865 auEndOffsets[0] = data.ReadUInt64();
866 auEndOffsets[1] = data.ReadUInt64();
867 auEndOffsets[2] = data.ReadUInt64();
868 } else {
869 auStartOffsets[0] = data.ReadUInt32();
870 auStartOffsets[1] = data.ReadUInt32();
871 auStartOffsets[2] = data.ReadUInt32();
873 auEndOffsets[0] = data.ReadUInt32();
874 auEndOffsets[1] = data.ReadUInt32();
875 auEndOffsets[2] = data.ReadUInt32();
878 for (unsigned int i = 0; i < itemsof(auStartOffsets); i++) {
879 AddDebugLogLineN(logClient,
880 CFormat(wxT("Client %s requests %d File block %d-%d (%d bytes):"))
881 % GetFullIP() % i % auStartOffsets[i] % auEndOffsets[i]
882 % (auEndOffsets[i] - auStartOffsets[i]));
883 if (auEndOffsets[i] > auStartOffsets[i]) {
884 Requested_Block_Struct* reqblock = new Requested_Block_Struct;
885 reqblock->StartOffset = auStartOffsets[i];
886 reqblock->EndOffset = auEndOffsets[i];
887 md4cpy(reqblock->FileID, reqfilehash.GetHash());
888 reqblock->transferred = 0;
889 AddReqBlock(reqblock);
890 } else {
891 if (auEndOffsets[i] != 0 || auStartOffsets[i] != 0) {
892 AddDebugLogLineN(logClient, wxT("Client request is invalid!"));
898 void CUpDownClient::ProcessRequestPartsPacketv2(const CMemFile& data) {
900 CMD4Hash reqfilehash = data.ReadHash();
902 uint8 numblocks = data.ReadUInt8();
904 for (int i = 0; i < numblocks; i++) {
905 Requested_Block_Struct* reqblock = new Requested_Block_Struct;
906 try {
907 reqblock->StartOffset = data.GetIntTagValue();
908 // We have to do +1, because the block matching uses that.
909 reqblock->EndOffset = data.GetIntTagValue() + 1;
910 if ((reqblock->StartOffset || reqblock->EndOffset) && (reqblock->StartOffset > reqblock->EndOffset)) {
911 AddDebugLogLineN(logClient, CFormat(wxT("Client %s request is invalid! %d / %d"))
912 % GetFullIP() % reqblock->StartOffset % reqblock->EndOffset);
913 throw wxString(wxT("Client request is invalid!"));
916 AddDebugLogLineN(logClient,
917 CFormat(wxT("Client %s requests %d File block %d-%d (%d bytes):"))
918 % GetFullIP() % i % reqblock->StartOffset % reqblock->EndOffset
919 % (reqblock->EndOffset - reqblock->StartOffset));
921 md4cpy(reqblock->FileID, reqfilehash.GetHash());
922 reqblock->transferred = 0;
923 AddReqBlock(reqblock);
924 } catch (...) {
925 delete reqblock;
926 throw;
930 // File_checked_for_headers