Upstream tarball 20080929
[amule.git] / src / UploadClient.cpp
blob0d0657fb37e4436ca1d05127f0050aca26b7b97d
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 <common/Format.h>
46 #include "ScopedPtr.h" // Needed for CScopedArray
47 #include "GuiEvents.h" // Needed for Notify_*
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 CFile file;
215 CPath fullname;
216 try {
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));
224 if (!srcfile) {
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.
230 #if 0
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.
235 return;
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.
241 #endif
243 // Get the full path to the '.part' file
244 fullname = dynamic_cast<CPartFile*>(srcfile)->GetFullName().RemoveExt();
245 } else {
246 fullname = srcfile->GetFilePath().JoinPaths(srcfile->GetFileName());
249 uint64 togo;
250 if (currentblock->StartOffset > currentblock->EndOffset){
251 togo = currentblock->EndOffset + (srcfile->GetFileSize() - currentblock->StartOffset);
252 } else {
253 togo = currentblock->EndOffset - currentblock->StartOffset;
255 if (srcfile->IsPartFile() && !((CPartFile*)srcfile)->IsComplete(currentblock->StartOffset,currentblock->EndOffset-1)) {
256 throw wxString(wxT("Asked for incomplete block "));
260 if (togo > EMBLOCKSIZE * 3) {
261 throw wxString(wxT("Client requested too large of a block."));
264 CScopedArray<byte> filedata(NULL);
265 if (!srcfile->IsPartFile()){
266 if ( !file.Open(fullname, CFile::read) ) {
267 // The file was most likely moved/deleted. However it is likely that the
268 // same is true for other files, so we recheck all shared files.
269 AddLogLineM( false, CFormat( _("Failed to open file (%s), removing from list of shared files.") ) % srcfile->GetFileName() );
270 theApp->sharedfiles->RemoveFile(srcfile);
272 throw wxString(wxT("Failed to open requested file: Removing from list of shared files!"));
275 file.Seek(currentblock->StartOffset, wxFromStart);
277 filedata.reset(new byte[togo + 500]);
278 file.Read(filedata.get(), togo);
279 file.Close();
280 } else {
281 CPartFile* partfile = (CPartFile*)srcfile;
282 partfile->m_hpartfile.Seek(currentblock->StartOffset);
284 filedata.reset(new byte[togo + 500]);
285 partfile->m_hpartfile.Read(filedata.get(), togo);
286 // Partfile should NOT be closed!!!
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(filedata.get(), togo,currentblock);
302 } else {
303 CreateStandartPackets(filedata.get(), 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, wxT("Client '") + GetUserName() + wxT("' caused error while creating packet (") + error + wxT(") - disconnecting client"));
320 } catch (const CIOFailureException& error) {
321 AddDebugLogLineM(true, logClient, wxT("IO failure while reading requested file: ") + error.what());
322 } catch (const CEOFException& WXUNUSED(error)) {
323 AddDebugLogLineM(true, logClient, GetClientFullInfo() + wxT(" requested file-data at an invalid position - disconnecting"));
326 // Error occured.
327 theApp->uploadqueue->RemoveFromUploadQueue(this);
331 void CUpDownClient::CreateStandartPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock)
333 uint32 nPacketSize;
335 CMemFile memfile(buffer, togo);
336 if (togo > 10240) {
337 nPacketSize = togo/(uint32)(togo/10240);
338 } else {
339 nPacketSize = togo;
342 while (togo){
343 if (togo < nPacketSize*2) {
344 nPacketSize = togo;
347 wxASSERT(nPacketSize);
348 togo -= nPacketSize;
350 uint64 endpos = (currentblock->EndOffset - togo);
351 uint64 startpos = endpos - nPacketSize;
353 bool bLargeBlocks = (startpos > 0xFFFFFFFF) || (endpos > 0xFFFFFFFF);
355 CMemFile data(nPacketSize + 16 + 2 * (bLargeBlocks ? 8 :4));
356 data.WriteHash(GetUploadFileID());
357 if (bLargeBlocks) {
358 data.WriteUInt64(startpos);
359 data.WriteUInt64(endpos);
360 } else {
361 data.WriteUInt32(startpos);
362 data.WriteUInt32(endpos);
364 char *tempbuf = new char[nPacketSize];
365 memfile.Read(tempbuf, nPacketSize);
366 data.Write(tempbuf, nPacketSize);
367 delete [] tempbuf;
368 CPacket* packet = new CPacket(data, (bLargeBlocks ? OP_EMULEPROT : OP_EDONKEYPROT), (bLargeBlocks ? (uint8)OP_SENDINGPART_I64 : (uint8)OP_SENDINGPART));
369 theStats::AddUpOverheadFileRequest(16 + 2 * (bLargeBlocks ? 8 :4));
370 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize);
371 AddDebugLogLineM( false, logLocalClient, wxString::Format(wxT("Local Client: %s to "),(bLargeBlocks ? wxT("OP_SENDINGPART_I64") : wxT("OP_SENDINGPART"))) + GetFullIP() );
372 m_socket->SendPacket(packet,true,false, nPacketSize);
377 void CUpDownClient::CreatePackedPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock)
379 byte* output = new byte[togo+300];
380 uLongf newsize = togo+300;
381 uint16 result = compress2(output, &newsize, buffer, togo,9);
382 if (result != Z_OK || togo <= newsize){
383 delete[] output;
384 CreateStandartPackets(buffer, togo, currentblock);
385 return;
388 CMemFile memfile(output,newsize);
390 uint32 totalPayloadSize = 0;
391 uint32 oldSize = togo;
392 togo = newsize;
393 uint32 nPacketSize;
394 if (togo > 10240) {
395 nPacketSize = togo/(uint32)(togo/10240);
396 } else {
397 nPacketSize = togo;
400 while (togo) {
401 if (togo < nPacketSize*2) {
402 nPacketSize = togo;
404 togo -= nPacketSize;
406 bool isLargeBlock = (currentblock->StartOffset > 0xFFFFFFFF) || (currentblock->EndOffset > 0xFFFFFFFF);
408 CMemFile data(nPacketSize + 16 + (isLargeBlock ? 12 : 8));
409 data.WriteHash(GetUploadFileID());
410 if (isLargeBlock) {
411 data.WriteUInt64(currentblock->StartOffset);
412 } else {
413 data.WriteUInt32(currentblock->StartOffset);
415 data.WriteUInt32(newsize);
416 char *tempbuf = new char[nPacketSize];
417 memfile.Read(tempbuf, nPacketSize);
418 data.Write(tempbuf,nPacketSize);
419 delete [] tempbuf;
420 CPacket* packet = new CPacket(data, OP_EMULEPROT, (isLargeBlock ? OP_COMPRESSEDPART_I64 : OP_COMPRESSEDPART));
422 // approximate payload size
423 uint32 payloadSize = nPacketSize*oldSize/newsize;
425 if (togo == 0 && totalPayloadSize+payloadSize < oldSize) {
426 payloadSize = oldSize-totalPayloadSize;
429 totalPayloadSize += payloadSize;
431 // put packet directly on socket
432 theStats::AddUpOverheadFileRequest(24);
433 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize);
434 AddDebugLogLineM( false, logLocalClient, wxString::Format(wxT("Local Client: %s to "), (isLargeBlock ? wxT("OP_COMPRESSEDPART_I64") : wxT("OP_COMPRESSEDPART"))) + GetFullIP() );
435 m_socket->SendPacket(packet,true,false, payloadSize);
437 delete[] output;
441 void CUpDownClient::ProcessExtendedInfo(const CMemFile *data, CKnownFile *tempreqfile)
443 m_uploadingfile->UpdateUpPartsFrequency( this, false ); // Decrement
444 m_upPartStatus.clear();
445 m_nUpCompleteSourcesCount= 0;
447 if( GetExtendedRequestsVersion() == 0 ) {
448 // Something is coded wrong on this client if he's sending something it doesn't advertise.
449 return;
452 if (data->GetLength() == 16) {
453 // Wrong again. Advertised >0 but send a 0-type packet.
454 // But this time we'll disconnect it.
455 throw CInvalidPacket(wxT("Wrong size on extended info packet"));
458 uint16 nED2KUpPartCount = data->ReadUInt16();
459 if (!nED2KUpPartCount) {
460 m_upPartStatus.resize( tempreqfile->GetPartCount(), 0 );
461 } else {
462 if (tempreqfile->GetED2KPartCount() != nED2KUpPartCount) {
463 // We already checked if we are talking about the same file.. So if we get here, something really strange happened!
464 m_upPartStatus.clear();
465 return;
468 m_upPartStatus.resize( tempreqfile->GetPartCount(), 0 );
470 try {
471 uint16 done = 0;
472 while (done != m_upPartStatus.size()) {
473 uint8 toread = data->ReadUInt8();
474 for (sint32 i = 0;i != 8;i++){
475 m_upPartStatus[done] = (toread>>i)&1;
476 // We may want to use this for another feature..
477 // if (m_upPartStatus[done] && !tempreqfile->IsComplete(done*PARTSIZE,((done+1)*PARTSIZE)-1))
478 // bPartsNeeded = true;
479 done++;
480 if (done == m_upPartStatus.size()) {
481 break;
485 } catch (...) {
486 // We want the increment the frequency even if we didn't read everything
487 m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment
489 throw;
492 if (GetExtendedRequestsVersion() > 1) {
493 uint16 nCompleteCountLast = GetUpCompleteSourcesCount();
494 uint16 nCompleteCountNew = data->ReadUInt16();
495 SetUpCompleteSourcesCount(nCompleteCountNew);
496 if (nCompleteCountLast != nCompleteCountNew) {
497 tempreqfile->UpdatePartsInfo();
502 m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment
504 Notify_QlistRefreshClient(this);
508 void CUpDownClient::SetUploadFileID(CKnownFile* newreqfile)
510 if (m_uploadingfile == newreqfile) {
511 return;
512 } else if (m_uploadingfile) {
513 m_uploadingfile->RemoveUploadingClient(this);
514 m_uploadingfile->UpdateUpPartsFrequency(this, false); // Decrement
517 if (newreqfile) {
518 // This is a new file! update info
519 newreqfile->AddUploadingClient(this);
521 if (m_requpfileid != newreqfile->GetFileHash()) {
522 m_requpfileid = newreqfile->GetFileHash();
523 m_upPartStatus.clear();
524 m_upPartStatus.resize( newreqfile->GetPartCount(), 0 );
525 } else {
526 // this is the same file we already had assigned. Only update data.
527 newreqfile->UpdateUpPartsFrequency(this, true); // Increment
530 m_uploadingfile = newreqfile;
531 } else {
532 m_upPartStatus.clear();
533 m_nUpCompleteSourcesCount = 0;
534 // This clears m_uploadingfile and m_requpfileid
535 ClearUploadFileID();
540 void CUpDownClient::AddReqBlock(Requested_Block_Struct* reqblock)
542 if (GetUploadState() != US_UPLOADING) {
543 AddDebugLogLineM(false, logRemoteClient, wxT("UploadClient: Client tried to add requested block when not in upload slot! Prevented requested blocks from being added."));
544 delete reqblock;
545 return;
549 std::list<Requested_Block_Struct*>::iterator it = m_DoneBlocks_list.begin();
550 for (; it != m_DoneBlocks_list.end(); ++it) {
551 if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) {
552 delete reqblock;
553 return;
559 std::list<Requested_Block_Struct*>::iterator it = m_BlockRequests_queue.begin();
560 for (; it != m_BlockRequests_queue.end(); ++it) {
561 if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) {
562 delete reqblock;
563 return;
568 m_BlockRequests_queue.push_back(reqblock);
572 uint32 CUpDownClient::GetWaitStartTime() const
574 uint32 dwResult = 0;
576 if ( credits ) {
577 dwResult = credits->GetSecureWaitStartTime(GetIP());
579 if (dwResult > m_dwUploadTime && IsDownloading()) {
580 // This happens only if two clients with invalid securehash are in the queue - if at all
581 dwResult = m_dwUploadTime - 1;
585 return dwResult;
589 void CUpDownClient::SetWaitStartTime()
591 if ( credits ) {
592 credits->SetSecWaitStartTime(GetIP());
597 void CUpDownClient::ClearWaitStartTime()
599 if ( credits ) {
600 credits->ClearWaitStartTime();
605 uint32 CUpDownClient::SendBlockData()
607 uint32 curTick = ::GetTickCount();
608 uint64 sentBytesCompleteFile = 0;
609 uint64 sentBytesPartFile = 0;
610 uint64 sentBytesPayload = 0;
612 if (m_socket) {
613 CEMSocket* s = m_socket;
614 // uint32 uUpStatsPort = GetUserPort();
616 // Extended statistics information based on which client software and which port we sent this data to...
617 // This also updates the grand total for sent bytes, etc. And where this data came from.
618 sentBytesCompleteFile = s->GetSentBytesCompleteFileSinceLastCallAndReset();
619 sentBytesPartFile = s->GetSentBytesPartFileSinceLastCallAndReset();
620 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, false, true, sentBytesCompleteFile, (IsFriend() && GetFriendSlot()));
621 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, true, true, sentBytesPartFile, (IsFriend() && GetFriendSlot()));
623 m_nTransferredUp += sentBytesCompleteFile + sentBytesPartFile;
624 credits->AddUploaded(sentBytesCompleteFile + sentBytesPartFile, GetIP(), theApp->CryptoAvailable());
626 sentBytesPayload = s->GetSentPayloadSinceLastCallAndReset();
627 m_nCurQueueSessionPayloadUp += sentBytesPayload;
629 if (theApp->uploadqueue->CheckForTimeOver(this)) {
630 theApp->uploadqueue->RemoveFromUploadQueue(this, true);
631 SendOutOfPartReqsAndAddToWaitingQueue();
632 } else {
633 // read blocks from file and put on socket
634 CreateNextBlockPackage();
638 if(sentBytesCompleteFile + sentBytesPartFile > 0 ||
639 m_AvarageUDR_list.empty() || (curTick - m_AvarageUDR_list.back().timestamp) > 1*1000) {
640 // Store how much data we've transferred this round,
641 // to be able to calculate average speed later
642 // keep sum of all values in list up to date
643 TransferredData newitem = {sentBytesCompleteFile + sentBytesPartFile, curTick};
644 m_AvarageUDR_list.push_back(newitem);
645 m_nSumForAvgUpDataRate += sentBytesCompleteFile + sentBytesPartFile;
648 // remove to old values in list
649 while ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 10*1000) {
650 // keep sum of all values in list up to date
651 m_nSumForAvgUpDataRate -= m_AvarageUDR_list.front().datalen;
652 m_AvarageUDR_list.pop_front();
655 // Calculate average speed for this slot
656 if ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 0 && GetUpStartTimeDelay() > 2*1000) {
657 m_nUpDatarate = ((uint64)m_nSumForAvgUpDataRate*1000) / (curTick-m_AvarageUDR_list.front().timestamp);
658 } else {
659 // not enough values to calculate trustworthy speed. Use -1 to tell this
660 m_nUpDatarate = 0; //-1;
663 // Check if it's time to update the display.
664 m_cSendblock++;
665 if (m_cSendblock == 30){
666 m_cSendblock = 0;
667 Notify_UploadCtrlRefreshClient(this);
670 return sentBytesCompleteFile + sentBytesPartFile;
674 void CUpDownClient::SendOutOfPartReqsAndAddToWaitingQueue()
676 // Kry - this is actually taken from eMule, but makes a lot of sense ;)
678 //OP_OUTOFPARTREQS will tell the downloading client to go back to OnQueue..
679 //The main reason for this is that if we put the client back on queue and it goes
680 //back to the upload before the socket times out... We get a situation where the
681 //downloader thinks it already sent the requested blocks and the uploader thinks
682 //the downloader didn't send any request blocks. Then the connection times out..
683 //I did some tests with eDonkey also and it seems to work well with them also..
685 // Send this inmediately, don't queue.
686 CPacket* pPacket = new CPacket(OP_OUTOFPARTREQS, 0, OP_EDONKEYPROT);
687 theStats::AddUpOverheadFileRequest(pPacket->GetPacketSize());
688 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_OUTOFPARTREQS to ") + GetFullIP() );
689 SendPacket(pPacket, true, true);
691 theApp->uploadqueue->AddClientToQueue(this);
696 * See description for CEMSocket::TruncateQueues().
698 void CUpDownClient::FlushSendBlocks()
700 // Call this when you stop upload, or the socket might be not able to send
701 if (m_socket) { //socket may be NULL...
702 m_socket->TruncateQueues();
707 void CUpDownClient::SendHashsetPacket(const CMD4Hash& forfileid)
709 CKnownFile* file = theApp->sharedfiles->GetFileByID( forfileid );
710 bool from_dq = false;
711 if ( !file ) {
712 from_dq = true;
713 if ((file = theApp->downloadqueue->GetFileByID(forfileid)) == NULL) {
714 AddLogLineM(false, CFormat( _("Hashset requested for unknown file: %s") ) % forfileid.Encode() );
716 return;
720 if ( !file->GetHashCount() ) {
721 if (from_dq) {
722 AddDebugLogLineM(false, logRemoteClient, wxT("Requested hashset could not be found"));
723 return;
724 } else {
725 file = theApp->downloadqueue->GetFileByID(forfileid);
726 if (!(file && file->GetHashCount())) {
727 AddDebugLogLineM(false, logRemoteClient, wxT("Requested hashset could not be found"));
728 return;
733 CMemFile data(1024);
734 data.WriteHash(file->GetFileHash());
735 uint16 parts = file->GetHashCount();
736 data.WriteUInt16(parts);
737 for (int i = 0; i != parts; i++) {
738 data.WriteHash(file->GetPartHash(i));
740 CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_HASHSETANSWER);
741 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
742 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_HASHSETANSWER to ") + GetFullIP());
743 SendPacket(packet,true,true);
747 void CUpDownClient::ClearUploadBlockRequests()
749 FlushSendBlocks();
750 DeleteContents(m_BlockRequests_queue);
751 DeleteContents(m_DoneBlocks_list);
755 void CUpDownClient::SendRankingInfo(){
756 if (!ExtProtocolAvailable()) {
757 return;
760 uint16 nRank = theApp->uploadqueue->GetWaitingPosition(this);
761 if (!nRank) {
762 return;
765 CMemFile data;
766 data.WriteUInt16(nRank);
767 // Kry: what are these zero bytes for. are they really correct?
768 // Kry - Well, eMule does like that. I guess they're ok.
769 data.WriteUInt32(0); data.WriteUInt32(0); data.WriteUInt16(0);
770 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_QUEUERANKING);
771 theStats::AddUpOverheadOther(packet->GetPacketSize());
772 AddDebugLogLineM(false, logLocalClient, wxT("Local Client: OP_QUEUERANKING to ") + GetFullIP());
773 SendPacket(packet,true,true);
777 void CUpDownClient::SendCommentInfo(CKnownFile* file)
779 if (!m_bCommentDirty || file == NULL || !ExtProtocolAvailable() || m_byAcceptCommentVer < 1) {
780 return;
782 m_bCommentDirty = false;
784 // Truncate to max len.
785 wxString desc = file->GetFileComment().Left(MAXFILECOMMENTLEN);
786 uint8 rating = file->GetFileRating();
788 if ( file->GetFileRating() == 0 && desc.IsEmpty() ) {
789 return;
792 CMemFile data(256);
793 data.WriteUInt8(rating);
794 data.WriteString(desc, GetUnicodeSupport(), 4 /* size it's uint32 */);
796 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_FILEDESC);
797 theStats::AddUpOverheadOther(packet->GetPacketSize());
798 AddDebugLogLineM(false, logLocalClient, wxT("Local Client: OP_FILEDESC to ") + GetFullIP());
799 SendPacket(packet,true);
802 void CUpDownClient::UnBan(){
803 m_Aggressiveness = 0;
805 theApp->clientlist->AddTrackClient(this);
806 theApp->clientlist->RemoveBannedClient( GetIP() );
807 SetUploadState(US_NONE);
808 ClearWaitStartTime();
810 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
813 void CUpDownClient::Ban(){
814 theApp->clientlist->AddTrackClient(this);
815 theApp->clientlist->AddBannedClient( GetIP() );
817 AddDebugLogLineM( false, logClient, wxT("Client '") + GetUserName() + wxT("' seems to be an aggressive client and is banned from the uploadqueue"));
819 SetUploadState(US_BANNED);
821 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
822 Notify_QlistRefreshClient(this);
825 bool CUpDownClient::IsBanned() const
827 return ( (theApp->clientlist->IsBannedClient(GetIP()) ) && m_nDownloadState != DS_DOWNLOADING);
830 void CUpDownClient::CheckForAggressive()
832 uint32 cur_time = ::GetTickCount();
834 // First call, initalize
835 if ( !m_LastFileRequest ) {
836 m_LastFileRequest = cur_time;
837 return;
840 // Is this an aggressive request?
841 if ( ( cur_time - m_LastFileRequest ) < MIN_REQUESTTIME ) {
842 m_Aggressiveness += 3;
844 // Is the client EVIL?
845 if ( m_Aggressiveness >= 10 && (!IsBanned() && m_nDownloadState != DS_DOWNLOADING )) {
846 AddDebugLogLineM( false, logClient, CFormat( wxT("Aggressive client banned (score: %d): %s -- %s -- %s") )
847 % m_Aggressiveness
848 % m_Username
849 % m_strModVersion
850 % m_fullClientVerString );
851 Ban();
853 } else {
854 // Polite request, reward client
855 if ( m_Aggressiveness )
856 m_Aggressiveness--;
859 m_LastFileRequest = cur_time;
863 void CUpDownClient::SetUploadFileID(const CMD4Hash& new_id)
865 // Update the uploading file found
866 CKnownFile* uploadingfile = theApp->sharedfiles->GetFileByID(new_id);
867 if ( !uploadingfile ) {
868 // Can this really happen?
869 uploadingfile = theApp->downloadqueue->GetFileByID(new_id);
871 SetUploadFileID(uploadingfile); // This will update queue count on old and new file.
874 void CUpDownClient::ProcessRequestPartsPacket(const byte* pachPacket, uint32 nSize, bool largeblocks) {
876 CMemFile data(pachPacket, nSize);
878 CMD4Hash reqfilehash = data.ReadHash();
880 uint64 auStartOffsets[3];
881 uint64 auEndOffsets[3];
883 if (largeblocks) {
884 auStartOffsets[0] = data.ReadUInt64();
885 auStartOffsets[1] = data.ReadUInt64();
886 auStartOffsets[2] = data.ReadUInt64();
888 auEndOffsets[0] = data.ReadUInt64();
889 auEndOffsets[1] = data.ReadUInt64();
890 auEndOffsets[2] = data.ReadUInt64();
891 } else {
892 auStartOffsets[0] = data.ReadUInt32();
893 auStartOffsets[1] = data.ReadUInt32();
894 auStartOffsets[2] = data.ReadUInt32();
896 auEndOffsets[0] = data.ReadUInt32();
897 auEndOffsets[1] = data.ReadUInt32();
898 auEndOffsets[2] = data.ReadUInt32();
901 for (unsigned int i = 0; i < itemsof(auStartOffsets); i++) {
902 AddDebugLogLineM(false, logClient,
903 wxString::Format(wxT("Client requests %u"), i)
904 + wxT(" ") + wxString::Format(wxT("File block %u-%u (%d bytes):"), auStartOffsets[i], auEndOffsets[i], auEndOffsets[i] - auStartOffsets[i])
905 + wxT(" ") + GetFullIP());
906 if (auEndOffsets[i] > auStartOffsets[i]) {
907 Requested_Block_Struct* reqblock = new Requested_Block_Struct;
908 reqblock->StartOffset = auStartOffsets[i];
909 reqblock->EndOffset = auEndOffsets[i];
910 md4cpy(reqblock->FileID, reqfilehash.GetHash());
911 reqblock->transferred = 0;
912 AddReqBlock(reqblock);
913 } else {
914 if (auEndOffsets[i] != 0 || auStartOffsets[i] != 0) {
915 AddDebugLogLineM(false, logClient, wxT("Client request is invalid!"));
921 void CUpDownClient::ProcessRequestPartsPacketv2(const CMemFile& data) {
923 CMD4Hash reqfilehash = data.ReadHash();
925 uint8 numblocks = data.ReadUInt8();
927 for (int i = 0; i < numblocks; i++) {
928 Requested_Block_Struct* reqblock = new Requested_Block_Struct;
929 try {
930 reqblock->StartOffset = data.GetIntTagValue();
931 // We have to do +1, because the block matching uses that.
932 reqblock->EndOffset = data.GetIntTagValue() + 1;
933 if ((reqblock->StartOffset || reqblock->EndOffset) && (reqblock->StartOffset > reqblock->EndOffset)) {
934 AddDebugLogLineM(false, logClient, wxString::Format(wxT("Client request is invalid! %i / %i"),reqblock->StartOffset, reqblock->EndOffset));
935 throw wxString(wxT("Client request is invalid!"));
938 AddDebugLogLineM(false, logClient,
939 wxString::Format(wxT("Client requests %u"), i)
940 + wxT(" ") + wxString::Format(wxT("File block %u-%u (%d bytes):"),reqblock->StartOffset, reqblock->EndOffset, reqblock->EndOffset - reqblock->StartOffset)
941 += wxT(" ") + GetFullIP());
943 md4cpy(reqblock->FileID, reqfilehash.GetHash());
944 reqblock->transferred = 0;
945 AddReqBlock(reqblock);
946 } catch (...) {
947 delete reqblock;
948 throw;
952 // File_checked_for_headers