Upstream tarball 9920
[amule.git] / src / UploadClient.cpp
bloba7f4a6553e2f941cdffe075360f973f1c01bf55a
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 #ifndef CLIENT_GUI
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()) {
77 return 0;
80 if (credits == 0) {
81 return 0;
84 const CKnownFile* pFile = GetUploadFile();
85 if ( !pFile ) {
86 return 0;
89 // bad clients (see note in function)
90 if (IsBadGuy()) {
91 return 0;
94 // friend slot
95 if (IsFriend() && GetFriendSlot() && !HasLowID()) {
96 return 0x0FFFFFFF;
99 if (IsBanned())
100 return 0;
102 if (sysvalue && HasLowID() && !IsConnected()){
103 return 0;
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
111 if(pFile != NULL){
112 switch(pFile->GetUpPriority()) {
113 case PR_POWERSHARE: //added for powershare (deltaHF)
114 filepriority = 2500;
115 break; //end
116 case PR_VERYHIGH:
117 filepriority = 18;
118 break;
119 case PR_HIGH:
120 filepriority = 9;
121 break;
122 case PR_LOW:
123 filepriority = 6;
124 break;
125 case PR_VERYLOW:
126 filepriority = 2;
127 break;
128 case PR_NORMAL:
129 default:
130 filepriority = 7;
131 break;
134 // calculate score, based on waitingtime and other factors
135 float fBaseValue;
136 if (onlybasevalue) {
137 fBaseValue = 100;
138 } else if (!isdownloading) {
139 fBaseValue = (float)(::GetTickCount()-GetWaitStartTime())/1000;
140 } else {
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( m_dwUploadTime > GetWaitStartTime()); // Obviously
147 fBaseValue += (float)(::GetTickCount() - m_dwUploadTime > 900000)? 900000:1800000;
148 fBaseValue /= 1000;
151 float modif = GetScoreRatio();
152 fBaseValue *= modif;
154 if (!onlybasevalue) {
155 fBaseValue *= (float(filepriority)/10.0f);
157 if( (IsEmuleClient() || GetClientSoft() < 10) && m_byEmuleVersion <= 0x19) {
158 fBaseValue *= 0.5f;
160 return (uint32)fBaseValue;
162 #endif
164 // Checks if it is next requested block from another chunk of the actual file or from another file
166 // [Returns]
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()
208 try {
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));
216 if (!srcfile) {
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));
242 CFileArea area;
243 if (srcPartFile) {
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"));
251 } else {
252 CFileAutoClose file;
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);
263 area.CheckError();
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);
270 } else {
271 CreateStandartPackets(area.GetBuffer(), togo, currentblock);
274 // file statistic
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);
285 return;
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"));
296 // Error occured.
297 theApp->uploadqueue->RemoveFromUploadQueue(this);
301 void CUpDownClient::CreateStandartPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock)
303 uint32 nPacketSize;
305 CMemFile memfile(buffer, togo);
306 if (togo > 10240) {
307 nPacketSize = togo/(uint32)(togo/10240);
308 } else {
309 nPacketSize = togo;
312 while (togo){
313 if (togo < nPacketSize*2) {
314 nPacketSize = togo;
317 wxASSERT(nPacketSize);
318 togo -= 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());
327 if (bLargeBlocks) {
328 data.WriteUInt64(startpos);
329 data.WriteUInt64(endpos);
330 } else {
331 data.WriteUInt32(startpos);
332 data.WriteUInt32(endpos);
334 char *tempbuf = new char[nPacketSize];
335 memfile.Read(tempbuf, nPacketSize);
336 data.Write(tempbuf, nPacketSize);
337 delete [] tempbuf;
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 CreateStandartPackets(buffer, togo, currentblock);
356 return;
359 CMemFile memfile(output.get(), newsize);
361 uint32 totalPayloadSize = 0;
362 uint32 oldSize = togo;
363 togo = newsize;
364 uint32 nPacketSize;
365 if (togo > 10240) {
366 nPacketSize = togo/(uint32)(togo/10240);
367 } else {
368 nPacketSize = togo;
371 while (togo) {
372 if (togo < nPacketSize*2) {
373 nPacketSize = togo;
375 togo -= nPacketSize;
377 bool isLargeBlock = (currentblock->StartOffset > 0xFFFFFFFF) || (currentblock->EndOffset > 0xFFFFFFFF);
379 CMemFile data(nPacketSize + 16 + (isLargeBlock ? 12 : 8));
380 data.WriteHash(GetUploadFileID());
381 if (isLargeBlock) {
382 data.WriteUInt64(currentblock->StartOffset);
383 } else {
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);
390 delete [] tempbuf;
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.
421 return;
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.resize( tempreqfile->GetPartCount(), 0 );
433 } else {
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();
437 return;
440 m_upPartStatus.resize( tempreqfile->GetPartCount(), 0 );
442 try {
443 uint16 done = 0;
444 while (done != m_upPartStatus.size()) {
445 uint8 toread = data->ReadUInt8();
446 for (sint32 i = 0;i != 8;i++){
447 m_upPartStatus[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;
451 done++;
452 if (done == m_upPartStatus.size()) {
453 break;
457 } catch (...) {
458 // We want the increment the frequency even if we didn't read everything
459 m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment
461 throw;
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_QlistRefreshClient(this);
480 void CUpDownClient::SetUploadFileID(CKnownFile* newreqfile)
482 if (m_uploadingfile == newreqfile) {
483 return;
484 } else if (m_uploadingfile) {
485 m_uploadingfile->RemoveUploadingClient(this);
486 m_uploadingfile->UpdateUpPartsFrequency(this, false); // Decrement
489 if (newreqfile) {
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.clear();
496 m_upPartStatus.resize( newreqfile->GetPartCount(), 0 );
497 } else {
498 // this is the same file we already had assigned. Only update data.
499 newreqfile->UpdateUpPartsFrequency(this, true); // Increment
502 m_uploadingfile = newreqfile;
503 } else {
504 m_upPartStatus.clear();
505 m_nUpCompleteSourcesCount = 0;
506 // This clears m_uploadingfile and m_requpfileid
507 ClearUploadFileID();
512 void CUpDownClient::AddReqBlock(Requested_Block_Struct* reqblock)
514 if (GetUploadState() != US_UPLOADING) {
515 AddDebugLogLineM(false, logRemoteClient, wxT("UploadClient: Client tried to add requested block when not in upload slot! Prevented requested blocks from being added."));
516 delete reqblock;
517 return;
521 std::list<Requested_Block_Struct*>::iterator it = m_DoneBlocks_list.begin();
522 for (; it != m_DoneBlocks_list.end(); ++it) {
523 if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) {
524 delete reqblock;
525 return;
531 std::list<Requested_Block_Struct*>::iterator it = m_BlockRequests_queue.begin();
532 for (; it != m_BlockRequests_queue.end(); ++it) {
533 if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) {
534 delete reqblock;
535 return;
540 m_BlockRequests_queue.push_back(reqblock);
544 uint32 CUpDownClient::GetWaitStartTime() const
546 uint32 dwResult = 0;
548 if ( credits ) {
549 dwResult = credits->GetSecureWaitStartTime(GetIP());
551 if (dwResult > m_dwUploadTime && IsDownloading()) {
552 // This happens only if two clients with invalid securehash are in the queue - if at all
553 dwResult = m_dwUploadTime - 1;
557 return dwResult;
561 void CUpDownClient::SetWaitStartTime()
563 if ( credits ) {
564 credits->SetSecWaitStartTime(GetIP());
569 void CUpDownClient::ClearWaitStartTime()
571 if ( credits ) {
572 credits->ClearWaitStartTime();
577 void CUpDownClient::ResetSessionUp()
579 m_nCurSessionUp = m_nTransferredUp;
580 m_addedPayloadQueueSession = 0;
581 m_nCurQueueSessionPayloadUp = 0;
582 // If upload was resumed there can be a remaining payload in the socket
583 // causing (prepared - sent) getting negative. So reset the counter here.
584 if (m_socket) {
585 CEMSocket* s = m_socket;
586 s->GetSentPayloadSinceLastCallAndReset();
591 uint32 CUpDownClient::SendBlockData()
593 uint32 curTick = ::GetTickCount();
594 uint64 sentBytesCompleteFile = 0;
595 uint64 sentBytesPartFile = 0;
596 uint64 sentBytesPayload = 0;
598 if (m_socket) {
599 CEMSocket* s = m_socket;
600 // uint32 uUpStatsPort = GetUserPort();
602 // Extended statistics information based on which client software and which port we sent this data to...
603 // This also updates the grand total for sent bytes, etc. And where this data came from.
604 sentBytesCompleteFile = s->GetSentBytesCompleteFileSinceLastCallAndReset();
605 sentBytesPartFile = s->GetSentBytesPartFileSinceLastCallAndReset();
606 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, false, true, sentBytesCompleteFile, (IsFriend() && GetFriendSlot()));
607 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, true, true, sentBytesPartFile, (IsFriend() && GetFriendSlot()));
609 m_nTransferredUp += sentBytesCompleteFile + sentBytesPartFile;
610 credits->AddUploaded(sentBytesCompleteFile + sentBytesPartFile, GetIP(), theApp->CryptoAvailable());
612 sentBytesPayload = s->GetSentPayloadSinceLastCallAndReset();
613 m_nCurQueueSessionPayloadUp += sentBytesPayload;
615 if (theApp->uploadqueue->CheckForTimeOver(this)) {
616 theApp->uploadqueue->RemoveFromUploadQueue(this);
617 SendOutOfPartReqsAndAddToWaitingQueue();
618 } else {
619 // read blocks from file and put on socket
620 CreateNextBlockPackage();
624 if(sentBytesCompleteFile + sentBytesPartFile > 0 ||
625 m_AvarageUDR_list.empty() || (curTick - m_AvarageUDR_list.back().timestamp) > 1*1000) {
626 // Store how much data we've transferred this round,
627 // to be able to calculate average speed later
628 // keep sum of all values in list up to date
629 TransferredData newitem = {sentBytesCompleteFile + sentBytesPartFile, curTick};
630 m_AvarageUDR_list.push_back(newitem);
631 m_nSumForAvgUpDataRate += sentBytesCompleteFile + sentBytesPartFile;
634 // remove to old values in list
635 while ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 10*1000) {
636 // keep sum of all values in list up to date
637 m_nSumForAvgUpDataRate -= m_AvarageUDR_list.front().datalen;
638 m_AvarageUDR_list.pop_front();
641 // Calculate average speed for this slot
642 if ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 0 && GetUpStartTimeDelay() > 2*1000) {
643 m_nUpDatarate = ((uint64)m_nSumForAvgUpDataRate*1000) / (curTick-m_AvarageUDR_list.front().timestamp);
644 } else {
645 // not enough values to calculate trustworthy speed. Use -1 to tell this
646 m_nUpDatarate = 0; //-1;
649 // Check if it's time to update the display.
650 m_cSendblock++;
651 if (m_cSendblock == 30){
652 m_cSendblock = 0;
653 Notify_UploadCtrlRefreshClient(this);
656 return sentBytesCompleteFile + sentBytesPartFile;
660 void CUpDownClient::SendOutOfPartReqsAndAddToWaitingQueue()
662 // Kry - this is actually taken from eMule, but makes a lot of sense ;)
664 //OP_OUTOFPARTREQS will tell the downloading client to go back to OnQueue..
665 //The main reason for this is that if we put the client back on queue and it goes
666 //back to the upload before the socket times out... We get a situation where the
667 //downloader thinks it already sent the requested blocks and the uploader thinks
668 //the downloader didn't send any request blocks. Then the connection times out..
669 //I did some tests with eDonkey also and it seems to work well with them also..
671 // Send this inmediately, don't queue.
672 CPacket* pPacket = new CPacket(OP_OUTOFPARTREQS, 0, OP_EDONKEYPROT);
673 theStats::AddUpOverheadFileRequest(pPacket->GetPacketSize());
674 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_OUTOFPARTREQS to ") + GetFullIP() );
675 SendPacket(pPacket, true, true);
677 theApp->uploadqueue->AddClientToQueue(this);
682 * See description for CEMSocket::TruncateQueues().
684 void CUpDownClient::FlushSendBlocks()
686 // Call this when you stop upload, or the socket might be not able to send
687 if (m_socket) { //socket may be NULL...
688 m_socket->TruncateQueues();
693 void CUpDownClient::SendHashsetPacket(const CMD4Hash& forfileid)
695 CKnownFile* file = theApp->sharedfiles->GetFileByID( forfileid );
696 bool from_dq = false;
697 if ( !file ) {
698 from_dq = true;
699 if ((file = theApp->downloadqueue->GetFileByID(forfileid)) == NULL) {
700 AddLogLineM(false, CFormat( _("Hashset requested for unknown file: %s") ) % forfileid.Encode() );
702 return;
706 if ( !file->GetHashCount() ) {
707 if (from_dq) {
708 AddDebugLogLineM(false, logRemoteClient, wxT("Requested hashset could not be found"));
709 return;
710 } else {
711 file = theApp->downloadqueue->GetFileByID(forfileid);
712 if (!(file && file->GetHashCount())) {
713 AddDebugLogLineM(false, logRemoteClient, wxT("Requested hashset could not be found"));
714 return;
719 CMemFile data(1024);
720 data.WriteHash(file->GetFileHash());
721 uint16 parts = file->GetHashCount();
722 data.WriteUInt16(parts);
723 for (int i = 0; i != parts; i++) {
724 data.WriteHash(file->GetPartHash(i));
726 CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_HASHSETANSWER);
727 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
728 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_HASHSETANSWER to ") + GetFullIP());
729 SendPacket(packet,true,true);
733 void CUpDownClient::ClearUploadBlockRequests()
735 FlushSendBlocks();
736 DeleteContents(m_BlockRequests_queue);
737 DeleteContents(m_DoneBlocks_list);
741 void CUpDownClient::SendRankingInfo(){
742 if (!ExtProtocolAvailable()) {
743 return;
746 uint16 nRank = theApp->uploadqueue->GetWaitingPosition(this);
747 if (!nRank) {
748 return;
751 CMemFile data;
752 data.WriteUInt16(nRank);
753 // Kry: what are these zero bytes for. are they really correct?
754 // Kry - Well, eMule does like that. I guess they're ok.
755 data.WriteUInt32(0); data.WriteUInt32(0); data.WriteUInt16(0);
756 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_QUEUERANKING);
757 theStats::AddUpOverheadOther(packet->GetPacketSize());
758 AddDebugLogLineM(false, logLocalClient, wxT("Local Client: OP_QUEUERANKING to ") + GetFullIP());
759 SendPacket(packet,true,true);
763 void CUpDownClient::SendCommentInfo(CKnownFile* file)
765 if (!m_bCommentDirty || file == NULL || !ExtProtocolAvailable() || m_byAcceptCommentVer < 1) {
766 return;
768 m_bCommentDirty = false;
770 // Truncate to max len.
771 wxString desc = file->GetFileComment().Left(MAXFILECOMMENTLEN);
772 uint8 rating = file->GetFileRating();
774 if ( file->GetFileRating() == 0 && desc.IsEmpty() ) {
775 return;
778 CMemFile data(256);
779 data.WriteUInt8(rating);
780 data.WriteString(desc, GetUnicodeSupport(), 4 /* size it's uint32 */);
782 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_FILEDESC);
783 theStats::AddUpOverheadOther(packet->GetPacketSize());
784 AddDebugLogLineM(false, logLocalClient, wxT("Local Client: OP_FILEDESC to ") + GetFullIP());
785 SendPacket(packet,true);
788 void CUpDownClient::UnBan(){
789 m_Aggressiveness = 0;
791 theApp->clientlist->AddTrackClient(this);
792 theApp->clientlist->RemoveBannedClient( GetIP() );
793 SetUploadState(US_NONE);
794 ClearWaitStartTime();
796 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
799 void CUpDownClient::Ban(){
800 theApp->clientlist->AddTrackClient(this);
801 theApp->clientlist->AddBannedClient( GetIP() );
803 AddDebugLogLineM( false, logClient, wxT("Client '") + GetUserName() + wxT("' seems to be an aggressive client and is banned from the uploadqueue"));
805 SetUploadState(US_BANNED);
807 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
808 Notify_QlistRefreshClient(this);
811 bool CUpDownClient::IsBanned() const
813 return ( (theApp->clientlist->IsBannedClient(GetIP()) ) && m_nDownloadState != DS_DOWNLOADING);
816 void CUpDownClient::CheckForAggressive()
818 uint32 cur_time = ::GetTickCount();
820 // First call, initalize
821 if ( !m_LastFileRequest ) {
822 m_LastFileRequest = cur_time;
823 return;
826 // Is this an aggressive request?
827 if ( ( cur_time - m_LastFileRequest ) < MIN_REQUESTTIME ) {
828 m_Aggressiveness += 3;
830 // Is the client EVIL?
831 if ( m_Aggressiveness >= 10 && (!IsBanned() && m_nDownloadState != DS_DOWNLOADING )) {
832 AddDebugLogLineM( false, logClient, CFormat( wxT("Aggressive client banned (score: %d): %s -- %s -- %s") )
833 % m_Aggressiveness
834 % m_Username
835 % m_strModVersion
836 % m_fullClientVerString );
837 Ban();
839 } else {
840 // Polite request, reward client
841 if ( m_Aggressiveness )
842 m_Aggressiveness--;
845 m_LastFileRequest = cur_time;
849 void CUpDownClient::SetUploadFileID(const CMD4Hash& new_id)
851 // Update the uploading file found
852 CKnownFile* uploadingfile = theApp->sharedfiles->GetFileByID(new_id);
853 if ( !uploadingfile ) {
854 // Can this really happen?
855 uploadingfile = theApp->downloadqueue->GetFileByID(new_id);
857 SetUploadFileID(uploadingfile); // This will update queue count on old and new file.
860 void CUpDownClient::ProcessRequestPartsPacket(const byte* pachPacket, uint32 nSize, bool largeblocks) {
862 CMemFile data(pachPacket, nSize);
864 CMD4Hash reqfilehash = data.ReadHash();
866 uint64 auStartOffsets[3];
867 uint64 auEndOffsets[3];
869 if (largeblocks) {
870 auStartOffsets[0] = data.ReadUInt64();
871 auStartOffsets[1] = data.ReadUInt64();
872 auStartOffsets[2] = data.ReadUInt64();
874 auEndOffsets[0] = data.ReadUInt64();
875 auEndOffsets[1] = data.ReadUInt64();
876 auEndOffsets[2] = data.ReadUInt64();
877 } else {
878 auStartOffsets[0] = data.ReadUInt32();
879 auStartOffsets[1] = data.ReadUInt32();
880 auStartOffsets[2] = data.ReadUInt32();
882 auEndOffsets[0] = data.ReadUInt32();
883 auEndOffsets[1] = data.ReadUInt32();
884 auEndOffsets[2] = data.ReadUInt32();
887 for (unsigned int i = 0; i < itemsof(auStartOffsets); i++) {
888 AddDebugLogLineM(false, logClient,
889 CFormat(wxT("Client %s requests %d File block %d-%d (%d bytes):"))
890 % GetFullIP() % i % auStartOffsets[i] % auEndOffsets[i]
891 % (auEndOffsets[i] - auStartOffsets[i]));
892 if (auEndOffsets[i] > auStartOffsets[i]) {
893 Requested_Block_Struct* reqblock = new Requested_Block_Struct;
894 reqblock->StartOffset = auStartOffsets[i];
895 reqblock->EndOffset = auEndOffsets[i];
896 md4cpy(reqblock->FileID, reqfilehash.GetHash());
897 reqblock->transferred = 0;
898 AddReqBlock(reqblock);
899 } else {
900 if (auEndOffsets[i] != 0 || auStartOffsets[i] != 0) {
901 AddDebugLogLineM(false, logClient, wxT("Client request is invalid!"));
907 void CUpDownClient::ProcessRequestPartsPacketv2(const CMemFile& data) {
909 CMD4Hash reqfilehash = data.ReadHash();
911 uint8 numblocks = data.ReadUInt8();
913 for (int i = 0; i < numblocks; i++) {
914 Requested_Block_Struct* reqblock = new Requested_Block_Struct;
915 try {
916 reqblock->StartOffset = data.GetIntTagValue();
917 // We have to do +1, because the block matching uses that.
918 reqblock->EndOffset = data.GetIntTagValue() + 1;
919 if ((reqblock->StartOffset || reqblock->EndOffset) && (reqblock->StartOffset > reqblock->EndOffset)) {
920 AddDebugLogLineM(false, logClient, CFormat(wxT("Client %s request is invalid! %d / %d"))
921 % GetFullIP() % reqblock->StartOffset % reqblock->EndOffset);
922 throw wxString(wxT("Client request is invalid!"));
925 AddDebugLogLineM(false, logClient,
926 CFormat(wxT("Client %s requests %d File block %d-%d (%d bytes):"))
927 % GetFullIP() % i % reqblock->StartOffset % reqblock->EndOffset
928 % (reqblock->EndOffset - reqblock->StartOffset));
930 md4cpy(reqblock->FileID, reqfilehash.GetHash());
931 reqblock->transferred = 0;
932 AddReqBlock(reqblock);
933 } catch (...) {
934 delete reqblock;
935 throw;
939 // File_checked_for_headers