Upstream tarball 9667
[amule.git] / src / UploadClient.cpp
blob2583ec00c1ca1b9c8ffa15bd95abe817cf13dda4
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 // 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.
209 if(m_BlockRequests_queue.empty() || // There are no new blocks requested
210 ((m_addedPayloadQueueSession > GetQueueSessionPayloadUp()) && m_addedPayloadQueueSession-GetQueueSessionPayloadUp() > 50*1024)) { // the buffered data is large enough allready
211 return;
214 CPath fullname;
215 try {
216 // Buffer new data if current buffer is less than 100 KBytes
217 while ((!m_BlockRequests_queue.empty()) &&
218 (m_addedPayloadQueueSession <= GetQueueSessionPayloadUp() || m_addedPayloadQueueSession-GetQueueSessionPayloadUp() < 100*1024)) {
220 Requested_Block_Struct* currentblock = m_BlockRequests_queue.front();
221 CKnownFile* srcfile = theApp->sharedfiles->GetFileByID(CMD4Hash(currentblock->FileID));
223 if (!srcfile) {
224 throw wxString(wxT("requested file not found"));
227 if (srcfile->IsPartFile() && ((CPartFile*)srcfile)->GetStatus() != PS_COMPLETE) {
228 //#warning This seems a good idea from eMule. We must import this.
229 #if 0
230 // Do not access a part file, if it is currently moved into the incoming directory.
231 // Because the moving of part file into the incoming directory may take a noticable
232 // amount of time, we can not wait for 'm_FileCompleteMutex' and block the main thread.
233 if (!((CPartFile*)srcfile)->m_FileCompleteMutex.Lock(0)){ // just do a quick test of the mutex's state and return if it's locked.
234 return;
236 lockFile.m_pObject = &((CPartFile*)srcfile)->m_FileCompleteMutex;
237 // If it's a part file which we are uploading the file remains locked until we've read the
238 // current block. This way the file completion thread can not (try to) "move" the file into
239 // the incoming directory.
240 #endif
242 // Get the full path to the '.part' file
243 fullname = dynamic_cast<CPartFile*>(srcfile)->GetFullName().RemoveExt();
244 } else {
245 fullname = srcfile->GetFilePath().JoinPaths(srcfile->GetFileName());
248 uint64 togo;
249 // THIS EndOffset points BEHIND the last byte requested
250 // (other than the offsets used in the PartFile code)
251 if (currentblock->EndOffset > srcfile->GetFileSize()) {
252 throw wxString(CFormat(wxT("Asked for data up to %d beyond end of file (%d)"))
253 % currentblock->EndOffset % srcfile->GetFileSize());
254 } else if (currentblock->StartOffset > currentblock->EndOffset) {
255 throw wxString(CFormat(wxT("Asked for invalid block (start %d > end %d)"))
256 % currentblock->StartOffset % currentblock->EndOffset);
257 } else {
258 togo = currentblock->EndOffset - currentblock->StartOffset;
260 if (srcfile->IsPartFile() && !((CPartFile*)srcfile)->IsComplete(currentblock->StartOffset,currentblock->EndOffset-1)) {
261 throw wxString(CFormat(wxT("Asked for incomplete block (%d - %d)"))
262 % currentblock->StartOffset % (currentblock->EndOffset-1));
266 if (togo > EMBLOCKSIZE * 3) {
267 throw wxString(CFormat(wxT("Client requested too large block (%d > %d)"))
268 % togo % (EMBLOCKSIZE * 3));
271 CFileArea area;
272 if (!srcfile->IsPartFile()){
273 CFileAutoClose file;
274 if ( !file.Open(fullname, CFile::read) ) {
275 // The file was most likely moved/deleted. However it is likely that the
276 // same is true for other files, so we recheck all shared files.
277 AddLogLineM( false, CFormat( _("Failed to open file (%s), removing from list of shared files.") ) % srcfile->GetFileName() );
278 theApp->sharedfiles->RemoveFile(srcfile);
280 throw wxString(wxT("Failed to open requested file: Removing from list of shared files!"));
282 area.ReadAt(file, currentblock->StartOffset, togo);
283 } else {
284 if (!((CPartFile*)srcfile)->ReadData(area, currentblock->StartOffset, togo))
285 throw wxString(wxT("Failed to read from requested partfile"));
287 area.CheckError();
289 //#warning Part of the above import.
290 #if 0
291 if (lockFile.m_pObject){
292 lockFile.m_pObject->Unlock(); // Unlock the (part) file as soon as we are done with accessing it.
293 lockFile.m_pObject = NULL;
295 #endif
297 SetUploadFileID(srcfile);
299 // check extention to decide whether to compress or not
300 if (m_byDataCompVer == 1 && GetFiletype(srcfile->GetFileName()) != ftArchive) {
301 CreatePackedPackets(area.GetBuffer(), togo, currentblock);
302 } else {
303 CreateStandartPackets(area.GetBuffer(), togo, currentblock);
306 // file statistic
307 srcfile->statistic.AddTransferred(togo);
309 m_addedPayloadQueueSession += togo;
311 Requested_Block_Struct* block = m_BlockRequests_queue.front();
313 m_BlockRequests_queue.pop_front();
314 m_DoneBlocks_list.push_front(block);
317 return;
318 } catch (const wxString& error) {
319 AddDebugLogLineM(false, logClient,
320 CFormat(wxT("Client '%s' (%s) caused error while creating packet (%s) - disconnecting client"))
321 % GetUserName() % GetFullIP() % error);
322 } catch (const CIOFailureException& error) {
323 AddDebugLogLineM(true, logClient, wxT("IO failure while reading requested file: ") + error.what());
324 } catch (const CEOFException& WXUNUSED(error)) {
325 AddDebugLogLineM(true, logClient, GetClientFullInfo() + wxT(" requested file-data at an invalid position - disconnecting"));
328 // Error occured.
329 theApp->uploadqueue->RemoveFromUploadQueue(this);
333 void CUpDownClient::CreateStandartPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock)
335 uint32 nPacketSize;
337 CMemFile memfile(buffer, togo);
338 if (togo > 10240) {
339 nPacketSize = togo/(uint32)(togo/10240);
340 } else {
341 nPacketSize = togo;
344 while (togo){
345 if (togo < nPacketSize*2) {
346 nPacketSize = togo;
349 wxASSERT(nPacketSize);
350 togo -= nPacketSize;
352 uint64 endpos = (currentblock->EndOffset - togo);
353 uint64 startpos = endpos - nPacketSize;
355 bool bLargeBlocks = (startpos > 0xFFFFFFFF) || (endpos > 0xFFFFFFFF);
357 CMemFile data(nPacketSize + 16 + 2 * (bLargeBlocks ? 8 :4));
358 data.WriteHash(GetUploadFileID());
359 if (bLargeBlocks) {
360 data.WriteUInt64(startpos);
361 data.WriteUInt64(endpos);
362 } else {
363 data.WriteUInt32(startpos);
364 data.WriteUInt32(endpos);
366 char *tempbuf = new char[nPacketSize];
367 memfile.Read(tempbuf, nPacketSize);
368 data.Write(tempbuf, nPacketSize);
369 delete [] tempbuf;
370 CPacket* packet = new CPacket(data, (bLargeBlocks ? OP_EMULEPROT : OP_EDONKEYPROT), (bLargeBlocks ? (uint8)OP_SENDINGPART_I64 : (uint8)OP_SENDINGPART));
371 theStats::AddUpOverheadFileRequest(16 + 2 * (bLargeBlocks ? 8 :4));
372 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize);
373 AddDebugLogLineM(false, logLocalClient,
374 CFormat(wxT("Local Client: %s to %s"))
375 % (bLargeBlocks ? wxT("OP_SENDINGPART_I64") : wxT("OP_SENDINGPART")) % GetFullIP() );
376 m_socket->SendPacket(packet,true,false, nPacketSize);
381 void CUpDownClient::CreatePackedPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock)
383 uLongf newsize = togo+300;
384 CScopedArray<byte> output(newsize);
385 uint16 result = compress2(output.get(), &newsize, buffer, togo, 9);
386 if (result != Z_OK || togo <= newsize){
387 CreateStandartPackets(buffer, togo, currentblock);
388 return;
391 CMemFile memfile(output.get(), newsize);
393 uint32 totalPayloadSize = 0;
394 uint32 oldSize = togo;
395 togo = newsize;
396 uint32 nPacketSize;
397 if (togo > 10240) {
398 nPacketSize = togo/(uint32)(togo/10240);
399 } else {
400 nPacketSize = togo;
403 while (togo) {
404 if (togo < nPacketSize*2) {
405 nPacketSize = togo;
407 togo -= nPacketSize;
409 bool isLargeBlock = (currentblock->StartOffset > 0xFFFFFFFF) || (currentblock->EndOffset > 0xFFFFFFFF);
411 CMemFile data(nPacketSize + 16 + (isLargeBlock ? 12 : 8));
412 data.WriteHash(GetUploadFileID());
413 if (isLargeBlock) {
414 data.WriteUInt64(currentblock->StartOffset);
415 } else {
416 data.WriteUInt32(currentblock->StartOffset);
418 data.WriteUInt32(newsize);
419 char *tempbuf = new char[nPacketSize];
420 memfile.Read(tempbuf, nPacketSize);
421 data.Write(tempbuf,nPacketSize);
422 delete [] tempbuf;
423 CPacket* packet = new CPacket(data, OP_EMULEPROT, (isLargeBlock ? OP_COMPRESSEDPART_I64 : OP_COMPRESSEDPART));
425 // approximate payload size
426 uint32 payloadSize = nPacketSize*oldSize/newsize;
428 if (togo == 0 && totalPayloadSize+payloadSize < oldSize) {
429 payloadSize = oldSize-totalPayloadSize;
432 totalPayloadSize += payloadSize;
434 // put packet directly on socket
435 theStats::AddUpOverheadFileRequest(24);
436 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize);
437 AddDebugLogLineM(false, logLocalClient,
438 CFormat(wxT("Local Client: %s to %s"))
439 % (isLargeBlock ? wxT("OP_COMPRESSEDPART_I64") : wxT("OP_COMPRESSEDPART")) % GetFullIP() );
440 m_socket->SendPacket(packet,true,false, payloadSize);
445 void CUpDownClient::ProcessExtendedInfo(const CMemFile *data, CKnownFile *tempreqfile)
447 m_uploadingfile->UpdateUpPartsFrequency( this, false ); // Decrement
448 m_upPartStatus.clear();
449 m_nUpCompleteSourcesCount= 0;
451 if( GetExtendedRequestsVersion() == 0 ) {
452 // Something is coded wrong on this client if he's sending something it doesn't advertise.
453 return;
456 if (data->GetLength() == 16) {
457 // Wrong again. Advertised >0 but send a 0-type packet.
458 // But this time we'll disconnect it.
459 throw CInvalidPacket(wxT("Wrong size on extended info packet"));
462 uint16 nED2KUpPartCount = data->ReadUInt16();
463 if (!nED2KUpPartCount) {
464 m_upPartStatus.resize( tempreqfile->GetPartCount(), 0 );
465 } else {
466 if (tempreqfile->GetED2KPartCount() != nED2KUpPartCount) {
467 // We already checked if we are talking about the same file.. So if we get here, something really strange happened!
468 m_upPartStatus.clear();
469 return;
472 m_upPartStatus.resize( tempreqfile->GetPartCount(), 0 );
474 try {
475 uint16 done = 0;
476 while (done != m_upPartStatus.size()) {
477 uint8 toread = data->ReadUInt8();
478 for (sint32 i = 0;i != 8;i++){
479 m_upPartStatus[done] = (toread>>i)&1;
480 // We may want to use this for another feature..
481 // if (m_upPartStatus[done] && !tempreqfile->IsComplete(done*PARTSIZE,((done+1)*PARTSIZE)-1))
482 // bPartsNeeded = true;
483 done++;
484 if (done == m_upPartStatus.size()) {
485 break;
489 } catch (...) {
490 // We want the increment the frequency even if we didn't read everything
491 m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment
493 throw;
496 if (GetExtendedRequestsVersion() > 1) {
497 uint16 nCompleteCountLast = GetUpCompleteSourcesCount();
498 uint16 nCompleteCountNew = data->ReadUInt16();
499 SetUpCompleteSourcesCount(nCompleteCountNew);
500 if (nCompleteCountLast != nCompleteCountNew) {
501 tempreqfile->UpdatePartsInfo();
506 m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment
508 Notify_QlistRefreshClient(this);
512 void CUpDownClient::SetUploadFileID(CKnownFile* newreqfile)
514 if (m_uploadingfile == newreqfile) {
515 return;
516 } else if (m_uploadingfile) {
517 m_uploadingfile->RemoveUploadingClient(this);
518 m_uploadingfile->UpdateUpPartsFrequency(this, false); // Decrement
521 if (newreqfile) {
522 // This is a new file! update info
523 newreqfile->AddUploadingClient(this);
525 if (m_requpfileid != newreqfile->GetFileHash()) {
526 m_requpfileid = newreqfile->GetFileHash();
527 m_upPartStatus.clear();
528 m_upPartStatus.resize( newreqfile->GetPartCount(), 0 );
529 } else {
530 // this is the same file we already had assigned. Only update data.
531 newreqfile->UpdateUpPartsFrequency(this, true); // Increment
534 m_uploadingfile = newreqfile;
535 } else {
536 m_upPartStatus.clear();
537 m_nUpCompleteSourcesCount = 0;
538 // This clears m_uploadingfile and m_requpfileid
539 ClearUploadFileID();
544 void CUpDownClient::AddReqBlock(Requested_Block_Struct* reqblock)
546 if (GetUploadState() != US_UPLOADING) {
547 AddDebugLogLineM(false, logRemoteClient, wxT("UploadClient: Client tried to add requested block when not in upload slot! Prevented requested blocks from being added."));
548 delete reqblock;
549 return;
553 std::list<Requested_Block_Struct*>::iterator it = m_DoneBlocks_list.begin();
554 for (; it != m_DoneBlocks_list.end(); ++it) {
555 if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) {
556 delete reqblock;
557 return;
563 std::list<Requested_Block_Struct*>::iterator it = m_BlockRequests_queue.begin();
564 for (; it != m_BlockRequests_queue.end(); ++it) {
565 if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) {
566 delete reqblock;
567 return;
572 m_BlockRequests_queue.push_back(reqblock);
576 uint32 CUpDownClient::GetWaitStartTime() const
578 uint32 dwResult = 0;
580 if ( credits ) {
581 dwResult = credits->GetSecureWaitStartTime(GetIP());
583 if (dwResult > m_dwUploadTime && IsDownloading()) {
584 // This happens only if two clients with invalid securehash are in the queue - if at all
585 dwResult = m_dwUploadTime - 1;
589 return dwResult;
593 void CUpDownClient::SetWaitStartTime()
595 if ( credits ) {
596 credits->SetSecWaitStartTime(GetIP());
601 void CUpDownClient::ClearWaitStartTime()
603 if ( credits ) {
604 credits->ClearWaitStartTime();
609 uint32 CUpDownClient::SendBlockData()
611 uint32 curTick = ::GetTickCount();
612 uint64 sentBytesCompleteFile = 0;
613 uint64 sentBytesPartFile = 0;
614 uint64 sentBytesPayload = 0;
616 if (m_socket) {
617 CEMSocket* s = m_socket;
618 // uint32 uUpStatsPort = GetUserPort();
620 // Extended statistics information based on which client software and which port we sent this data to...
621 // This also updates the grand total for sent bytes, etc. And where this data came from.
622 sentBytesCompleteFile = s->GetSentBytesCompleteFileSinceLastCallAndReset();
623 sentBytesPartFile = s->GetSentBytesPartFileSinceLastCallAndReset();
624 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, false, true, sentBytesCompleteFile, (IsFriend() && GetFriendSlot()));
625 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, true, true, sentBytesPartFile, (IsFriend() && GetFriendSlot()));
627 m_nTransferredUp += sentBytesCompleteFile + sentBytesPartFile;
628 credits->AddUploaded(sentBytesCompleteFile + sentBytesPartFile, GetIP(), theApp->CryptoAvailable());
630 sentBytesPayload = s->GetSentPayloadSinceLastCallAndReset();
631 m_nCurQueueSessionPayloadUp += sentBytesPayload;
633 if (theApp->uploadqueue->CheckForTimeOver(this)) {
634 theApp->uploadqueue->RemoveFromUploadQueue(this, true);
635 SendOutOfPartReqsAndAddToWaitingQueue();
636 } else {
637 // read blocks from file and put on socket
638 CreateNextBlockPackage();
642 if(sentBytesCompleteFile + sentBytesPartFile > 0 ||
643 m_AvarageUDR_list.empty() || (curTick - m_AvarageUDR_list.back().timestamp) > 1*1000) {
644 // Store how much data we've transferred this round,
645 // to be able to calculate average speed later
646 // keep sum of all values in list up to date
647 TransferredData newitem = {sentBytesCompleteFile + sentBytesPartFile, curTick};
648 m_AvarageUDR_list.push_back(newitem);
649 m_nSumForAvgUpDataRate += sentBytesCompleteFile + sentBytesPartFile;
652 // remove to old values in list
653 while ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 10*1000) {
654 // keep sum of all values in list up to date
655 m_nSumForAvgUpDataRate -= m_AvarageUDR_list.front().datalen;
656 m_AvarageUDR_list.pop_front();
659 // Calculate average speed for this slot
660 if ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 0 && GetUpStartTimeDelay() > 2*1000) {
661 m_nUpDatarate = ((uint64)m_nSumForAvgUpDataRate*1000) / (curTick-m_AvarageUDR_list.front().timestamp);
662 } else {
663 // not enough values to calculate trustworthy speed. Use -1 to tell this
664 m_nUpDatarate = 0; //-1;
667 // Check if it's time to update the display.
668 m_cSendblock++;
669 if (m_cSendblock == 30){
670 m_cSendblock = 0;
671 Notify_UploadCtrlRefreshClient(this);
674 return sentBytesCompleteFile + sentBytesPartFile;
678 void CUpDownClient::SendOutOfPartReqsAndAddToWaitingQueue()
680 // Kry - this is actually taken from eMule, but makes a lot of sense ;)
682 //OP_OUTOFPARTREQS will tell the downloading client to go back to OnQueue..
683 //The main reason for this is that if we put the client back on queue and it goes
684 //back to the upload before the socket times out... We get a situation where the
685 //downloader thinks it already sent the requested blocks and the uploader thinks
686 //the downloader didn't send any request blocks. Then the connection times out..
687 //I did some tests with eDonkey also and it seems to work well with them also..
689 // Send this inmediately, don't queue.
690 CPacket* pPacket = new CPacket(OP_OUTOFPARTREQS, 0, OP_EDONKEYPROT);
691 theStats::AddUpOverheadFileRequest(pPacket->GetPacketSize());
692 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_OUTOFPARTREQS to ") + GetFullIP() );
693 SendPacket(pPacket, true, true);
695 theApp->uploadqueue->AddClientToQueue(this);
700 * See description for CEMSocket::TruncateQueues().
702 void CUpDownClient::FlushSendBlocks()
704 // Call this when you stop upload, or the socket might be not able to send
705 if (m_socket) { //socket may be NULL...
706 m_socket->TruncateQueues();
711 void CUpDownClient::SendHashsetPacket(const CMD4Hash& forfileid)
713 CKnownFile* file = theApp->sharedfiles->GetFileByID( forfileid );
714 bool from_dq = false;
715 if ( !file ) {
716 from_dq = true;
717 if ((file = theApp->downloadqueue->GetFileByID(forfileid)) == NULL) {
718 AddLogLineM(false, CFormat( _("Hashset requested for unknown file: %s") ) % forfileid.Encode() );
720 return;
724 if ( !file->GetHashCount() ) {
725 if (from_dq) {
726 AddDebugLogLineM(false, logRemoteClient, wxT("Requested hashset could not be found"));
727 return;
728 } else {
729 file = theApp->downloadqueue->GetFileByID(forfileid);
730 if (!(file && file->GetHashCount())) {
731 AddDebugLogLineM(false, logRemoteClient, wxT("Requested hashset could not be found"));
732 return;
737 CMemFile data(1024);
738 data.WriteHash(file->GetFileHash());
739 uint16 parts = file->GetHashCount();
740 data.WriteUInt16(parts);
741 for (int i = 0; i != parts; i++) {
742 data.WriteHash(file->GetPartHash(i));
744 CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_HASHSETANSWER);
745 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
746 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_HASHSETANSWER to ") + GetFullIP());
747 SendPacket(packet,true,true);
751 void CUpDownClient::ClearUploadBlockRequests()
753 FlushSendBlocks();
754 DeleteContents(m_BlockRequests_queue);
755 DeleteContents(m_DoneBlocks_list);
759 void CUpDownClient::SendRankingInfo(){
760 if (!ExtProtocolAvailable()) {
761 return;
764 uint16 nRank = theApp->uploadqueue->GetWaitingPosition(this);
765 if (!nRank) {
766 return;
769 CMemFile data;
770 data.WriteUInt16(nRank);
771 // Kry: what are these zero bytes for. are they really correct?
772 // Kry - Well, eMule does like that. I guess they're ok.
773 data.WriteUInt32(0); data.WriteUInt32(0); data.WriteUInt16(0);
774 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_QUEUERANKING);
775 theStats::AddUpOverheadOther(packet->GetPacketSize());
776 AddDebugLogLineM(false, logLocalClient, wxT("Local Client: OP_QUEUERANKING to ") + GetFullIP());
777 SendPacket(packet,true,true);
781 void CUpDownClient::SendCommentInfo(CKnownFile* file)
783 if (!m_bCommentDirty || file == NULL || !ExtProtocolAvailable() || m_byAcceptCommentVer < 1) {
784 return;
786 m_bCommentDirty = false;
788 // Truncate to max len.
789 wxString desc = file->GetFileComment().Left(MAXFILECOMMENTLEN);
790 uint8 rating = file->GetFileRating();
792 if ( file->GetFileRating() == 0 && desc.IsEmpty() ) {
793 return;
796 CMemFile data(256);
797 data.WriteUInt8(rating);
798 data.WriteString(desc, GetUnicodeSupport(), 4 /* size it's uint32 */);
800 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_FILEDESC);
801 theStats::AddUpOverheadOther(packet->GetPacketSize());
802 AddDebugLogLineM(false, logLocalClient, wxT("Local Client: OP_FILEDESC to ") + GetFullIP());
803 SendPacket(packet,true);
806 void CUpDownClient::UnBan(){
807 m_Aggressiveness = 0;
809 theApp->clientlist->AddTrackClient(this);
810 theApp->clientlist->RemoveBannedClient( GetIP() );
811 SetUploadState(US_NONE);
812 ClearWaitStartTime();
814 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
817 void CUpDownClient::Ban(){
818 theApp->clientlist->AddTrackClient(this);
819 theApp->clientlist->AddBannedClient( GetIP() );
821 AddDebugLogLineM( false, logClient, wxT("Client '") + GetUserName() + wxT("' seems to be an aggressive client and is banned from the uploadqueue"));
823 SetUploadState(US_BANNED);
825 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
826 Notify_QlistRefreshClient(this);
829 bool CUpDownClient::IsBanned() const
831 return ( (theApp->clientlist->IsBannedClient(GetIP()) ) && m_nDownloadState != DS_DOWNLOADING);
834 void CUpDownClient::CheckForAggressive()
836 uint32 cur_time = ::GetTickCount();
838 // First call, initalize
839 if ( !m_LastFileRequest ) {
840 m_LastFileRequest = cur_time;
841 return;
844 // Is this an aggressive request?
845 if ( ( cur_time - m_LastFileRequest ) < MIN_REQUESTTIME ) {
846 m_Aggressiveness += 3;
848 // Is the client EVIL?
849 if ( m_Aggressiveness >= 10 && (!IsBanned() && m_nDownloadState != DS_DOWNLOADING )) {
850 AddDebugLogLineM( false, logClient, CFormat( wxT("Aggressive client banned (score: %d): %s -- %s -- %s") )
851 % m_Aggressiveness
852 % m_Username
853 % m_strModVersion
854 % m_fullClientVerString );
855 Ban();
857 } else {
858 // Polite request, reward client
859 if ( m_Aggressiveness )
860 m_Aggressiveness--;
863 m_LastFileRequest = cur_time;
867 void CUpDownClient::SetUploadFileID(const CMD4Hash& new_id)
869 // Update the uploading file found
870 CKnownFile* uploadingfile = theApp->sharedfiles->GetFileByID(new_id);
871 if ( !uploadingfile ) {
872 // Can this really happen?
873 uploadingfile = theApp->downloadqueue->GetFileByID(new_id);
875 SetUploadFileID(uploadingfile); // This will update queue count on old and new file.
878 void CUpDownClient::ProcessRequestPartsPacket(const byte* pachPacket, uint32 nSize, bool largeblocks) {
880 CMemFile data(pachPacket, nSize);
882 CMD4Hash reqfilehash = data.ReadHash();
884 uint64 auStartOffsets[3];
885 uint64 auEndOffsets[3];
887 if (largeblocks) {
888 auStartOffsets[0] = data.ReadUInt64();
889 auStartOffsets[1] = data.ReadUInt64();
890 auStartOffsets[2] = data.ReadUInt64();
892 auEndOffsets[0] = data.ReadUInt64();
893 auEndOffsets[1] = data.ReadUInt64();
894 auEndOffsets[2] = data.ReadUInt64();
895 } else {
896 auStartOffsets[0] = data.ReadUInt32();
897 auStartOffsets[1] = data.ReadUInt32();
898 auStartOffsets[2] = data.ReadUInt32();
900 auEndOffsets[0] = data.ReadUInt32();
901 auEndOffsets[1] = data.ReadUInt32();
902 auEndOffsets[2] = data.ReadUInt32();
905 for (unsigned int i = 0; i < itemsof(auStartOffsets); i++) {
906 AddDebugLogLineM(false, logClient,
907 CFormat(wxT("Client %s requests %d File block %d-%d (%d bytes):"))
908 % GetFullIP() % i % auStartOffsets[i] % auEndOffsets[i]
909 % (auEndOffsets[i] - auStartOffsets[i]));
910 if (auEndOffsets[i] > auStartOffsets[i]) {
911 Requested_Block_Struct* reqblock = new Requested_Block_Struct;
912 reqblock->StartOffset = auStartOffsets[i];
913 reqblock->EndOffset = auEndOffsets[i];
914 md4cpy(reqblock->FileID, reqfilehash.GetHash());
915 reqblock->transferred = 0;
916 AddReqBlock(reqblock);
917 } else {
918 if (auEndOffsets[i] != 0 || auStartOffsets[i] != 0) {
919 AddDebugLogLineM(false, logClient, wxT("Client request is invalid!"));
925 void CUpDownClient::ProcessRequestPartsPacketv2(const CMemFile& data) {
927 CMD4Hash reqfilehash = data.ReadHash();
929 uint8 numblocks = data.ReadUInt8();
931 for (int i = 0; i < numblocks; i++) {
932 Requested_Block_Struct* reqblock = new Requested_Block_Struct;
933 try {
934 reqblock->StartOffset = data.GetIntTagValue();
935 // We have to do +1, because the block matching uses that.
936 reqblock->EndOffset = data.GetIntTagValue() + 1;
937 if ((reqblock->StartOffset || reqblock->EndOffset) && (reqblock->StartOffset > reqblock->EndOffset)) {
938 AddDebugLogLineM(false, logClient, CFormat(wxT("Client %s request is invalid! %d / %d"))
939 % GetFullIP() % reqblock->StartOffset % reqblock->EndOffset);
940 throw wxString(wxT("Client request is invalid!"));
943 AddDebugLogLineM(false, logClient,
944 CFormat(wxT("Client %s requests %d File block %d-%d (%d bytes):"))
945 % GetFullIP() % i % reqblock->StartOffset % reqblock->EndOffset
946 % (reqblock->EndOffset - reqblock->StartOffset));
948 md4cpy(reqblock->FileID, reqfilehash.GetHash());
949 reqblock->transferred = 0;
950 AddReqBlock(reqblock);
951 } catch (...) {
952 delete reqblock;
953 throw;
957 // File_checked_for_headers