Upstream tarball 9445
[amule.git] / src / UploadClient.cpp
blob2e9d515b01bbd4a10e34eb9d3b8c7a6ef32e01dc
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_*
48 #include "FileArea.h" // Needed for CFileArea
51 // members of CUpDownClient
52 // which are mainly used for uploading functions
54 void CUpDownClient::SetUploadState(uint8 eNewState)
56 if (eNewState != m_nUploadState) {
57 if (m_nUploadState == US_UPLOADING) {
58 // Reset upload data rate computation
59 m_nUpDatarate = 0;
60 m_nSumForAvgUpDataRate = 0;
61 m_AvarageUDR_list.clear();
63 if (eNewState == US_UPLOADING) {
64 m_fSentOutOfPartReqs = 0;
67 // don't add any final cleanups for US_NONE here
68 m_nUploadState = eNewState;
69 UpdateDisplayedInfo(true);
73 #ifndef CLIENT_GUI
74 uint32 CUpDownClient::GetScore(bool sysvalue, bool isdownloading, bool onlybasevalue) const
76 //TODO: complete this (friends, uploadspeed, amuleuser etc etc)
77 if (m_Username.IsEmpty()) {
78 return 0;
81 if (credits == 0) {
82 return 0;
85 const CKnownFile* pFile = GetUploadFile();
86 if ( !pFile ) {
87 return 0;
90 // bad clients (see note in function)
91 if (IsBadGuy()) {
92 return 0;
95 // friend slot
96 if (IsFriend() && GetFriendSlot() && !HasLowID()) {
97 return 0x0FFFFFFF;
100 if (IsBanned())
101 return 0;
103 if (sysvalue && HasLowID() && !IsConnected()){
104 return 0;
107 // TODO coded by tecxx & herbert, one yet unsolved problem here:
108 // sometimes a client asks for 2 files and there is no way to decide, which file the
109 // client finally gets. so it could happen that he is queued first because of a
110 // high prio file, but then asks for something completely different.
111 int filepriority = 10; // standard
112 if(pFile != NULL){
113 switch(pFile->GetUpPriority()) {
114 case PR_POWERSHARE: //added for powershare (deltaHF)
115 filepriority = 2500;
116 break; //end
117 case PR_VERYHIGH:
118 filepriority = 18;
119 break;
120 case PR_HIGH:
121 filepriority = 9;
122 break;
123 case PR_LOW:
124 filepriority = 6;
125 break;
126 case PR_VERYLOW:
127 filepriority = 2;
128 break;
129 case PR_NORMAL:
130 default:
131 filepriority = 7;
132 break;
135 // calculate score, based on waitingtime and other factors
136 float fBaseValue;
137 if (onlybasevalue) {
138 fBaseValue = 100;
139 } else if (!isdownloading) {
140 fBaseValue = (float)(::GetTickCount()-GetWaitStartTime())/1000;
141 } else {
142 // we dont want one client to download forever
143 // the first 15 min downloadtime counts as 15 min waitingtime and you get
144 // a 15 min bonus while you are in the first 15 min :)
145 // (to avoid 20 sec downloads) after this the score won't raise anymore
146 fBaseValue = (float)(m_dwUploadTime-GetWaitStartTime());
147 wxASSERT( m_dwUploadTime > GetWaitStartTime()); // Obviously
148 fBaseValue += (float)(::GetTickCount() - m_dwUploadTime > 900000)? 900000:1800000;
149 fBaseValue /= 1000;
152 float modif = GetScoreRatio();
153 fBaseValue *= modif;
155 if (!onlybasevalue) {
156 fBaseValue *= (float(filepriority)/10.0f);
158 if( (IsEmuleClient() || GetClientSoft() < 10) && m_byEmuleVersion <= 0x19) {
159 fBaseValue *= 0.5f;
161 return (uint32)fBaseValue;
163 #endif
165 // Checks if it is next requested block from another chunk of the actual file or from another file
167 // [Returns]
168 // true : Next requested block is from another different chunk or file than last downloaded block
169 // false: Next requested block is from same chunk that last downloaded block
170 bool CUpDownClient::IsDifferentPartBlock() const // [Tarod 12/22/2002]
172 bool different_part = false;
174 // Check if we have good lists and proceed to check for different chunks
175 if ((!m_BlockRequests_queue.empty()) && !m_DoneBlocks_list.empty())
177 Requested_Block_Struct* last_done_block = NULL;
178 Requested_Block_Struct* next_requested_block = NULL;
179 uint64 last_done_part = 0xffffffff;
180 uint64 next_requested_part = 0xffffffff;
183 // Get last block and next pending
184 last_done_block = m_DoneBlocks_list.front();
185 next_requested_block = m_BlockRequests_queue.front();
187 // Calculate corresponding parts to blocks
188 last_done_part = last_done_block->StartOffset / PARTSIZE;
189 next_requested_part = next_requested_block->StartOffset / PARTSIZE;
191 // Test is we are asking same file and same part
192 if ( last_done_part != next_requested_part) {
193 different_part = true;
194 AddDebugLogLineM(false, logClient, wxT("Session ended due to new chunk."));
197 if (md4cmp(last_done_block->FileID, next_requested_block->FileID) != 0) {
198 different_part = true;
199 AddDebugLogLineM(false, logClient, wxT("Session ended due to different file."));
203 return different_part;
207 void CUpDownClient::CreateNextBlockPackage()
209 // See if we can do an early return. There may be no new blocks to load from disk and add to buffer, or buffer may be large enough allready.
210 if(m_BlockRequests_queue.empty() || // There are no new blocks requested
211 ((m_addedPayloadQueueSession > GetQueueSessionPayloadUp()) && m_addedPayloadQueueSession-GetQueueSessionPayloadUp() > 50*1024)) { // the buffered data is large enough allready
212 return;
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->EndOffset >= srcfile->GetFileSize()) {
251 throw wxString(wxT("Asked for data beyond end of file"));
252 } else if (currentblock->StartOffset > currentblock->EndOffset) {
253 throw wxString(wxT("Asked for invalid block (start > end)"));
254 } else {
255 togo = currentblock->EndOffset - currentblock->StartOffset;
257 if (srcfile->IsPartFile() && !((CPartFile*)srcfile)->IsComplete(currentblock->StartOffset,currentblock->EndOffset-1)) {
258 throw wxString(wxT("Asked for incomplete block "));
262 if (togo > EMBLOCKSIZE * 3) {
263 throw wxString(wxT("Client requested too large of a block."));
266 CFileArea area;
267 if (!srcfile->IsPartFile()){
268 CFile file;
269 if ( !file.Open(fullname, CFile::read) ) {
270 // The file was most likely moved/deleted. However it is likely that the
271 // same is true for other files, so we recheck all shared files.
272 AddLogLineM( false, CFormat( _("Failed to open file (%s), removing from list of shared files.") ) % srcfile->GetFileName() );
273 theApp->sharedfiles->RemoveFile(srcfile);
275 throw wxString(wxT("Failed to open requested file: Removing from list of shared files!"));
277 area.Read(file, togo);
278 } else {
279 if (!((CPartFile*)srcfile)->ReadData(area, currentblock->StartOffset, togo))
280 throw wxString(wxT("Failed to read from requested partfile"));
283 //#warning Part of the above import.
284 #if 0
285 if (lockFile.m_pObject){
286 lockFile.m_pObject->Unlock(); // Unlock the (part) file as soon as we are done with accessing it.
287 lockFile.m_pObject = NULL;
289 #endif
291 SetUploadFileID(srcfile);
293 // check extention to decide whether to compress or not
294 if (m_byDataCompVer == 1 && GetFiletype(srcfile->GetFileName()) != ftArchive) {
295 CreatePackedPackets(area.GetBuffer(), togo, currentblock);
296 } else {
297 CreateStandartPackets(area.GetBuffer(), togo, currentblock);
300 // file statistic
301 srcfile->statistic.AddTransferred(togo);
303 m_addedPayloadQueueSession += togo;
305 Requested_Block_Struct* block = m_BlockRequests_queue.front();
307 m_BlockRequests_queue.pop_front();
308 m_DoneBlocks_list.push_front(block);
311 return;
312 } catch (const wxString& error) {
313 AddDebugLogLineM(false, logClient, wxT("Client '") + GetUserName() + wxT("' caused error while creating packet (") + error + wxT(") - disconnecting client"));
314 } catch (const CIOFailureException& error) {
315 AddDebugLogLineM(true, logClient, wxT("IO failure while reading requested file: ") + error.what());
316 } catch (const CEOFException& WXUNUSED(error)) {
317 AddDebugLogLineM(true, logClient, GetClientFullInfo() + wxT(" requested file-data at an invalid position - disconnecting"));
320 // Error occured.
321 theApp->uploadqueue->RemoveFromUploadQueue(this);
325 void CUpDownClient::CreateStandartPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock)
327 uint32 nPacketSize;
329 CMemFile memfile(buffer, togo);
330 if (togo > 10240) {
331 nPacketSize = togo/(uint32)(togo/10240);
332 } else {
333 nPacketSize = togo;
336 while (togo){
337 if (togo < nPacketSize*2) {
338 nPacketSize = togo;
341 wxASSERT(nPacketSize);
342 togo -= nPacketSize;
344 uint64 endpos = (currentblock->EndOffset - togo);
345 uint64 startpos = endpos - nPacketSize;
347 bool bLargeBlocks = (startpos > 0xFFFFFFFF) || (endpos > 0xFFFFFFFF);
349 CMemFile data(nPacketSize + 16 + 2 * (bLargeBlocks ? 8 :4));
350 data.WriteHash(GetUploadFileID());
351 if (bLargeBlocks) {
352 data.WriteUInt64(startpos);
353 data.WriteUInt64(endpos);
354 } else {
355 data.WriteUInt32(startpos);
356 data.WriteUInt32(endpos);
358 char *tempbuf = new char[nPacketSize];
359 memfile.Read(tempbuf, nPacketSize);
360 data.Write(tempbuf, nPacketSize);
361 delete [] tempbuf;
362 CPacket* packet = new CPacket(data, (bLargeBlocks ? OP_EMULEPROT : OP_EDONKEYPROT), (bLargeBlocks ? (uint8)OP_SENDINGPART_I64 : (uint8)OP_SENDINGPART));
363 theStats::AddUpOverheadFileRequest(16 + 2 * (bLargeBlocks ? 8 :4));
364 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize);
365 AddDebugLogLineM( false, logLocalClient, wxString::Format(wxT("Local Client: %s to "),(bLargeBlocks ? wxT("OP_SENDINGPART_I64") : wxT("OP_SENDINGPART"))) + GetFullIP() );
366 m_socket->SendPacket(packet,true,false, nPacketSize);
371 void CUpDownClient::CreatePackedPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock)
373 uLongf newsize = togo+300;
374 CScopedArray<byte> output(newsize);
375 uint16 result = compress2(output.get(), &newsize, buffer, togo, 9);
376 if (result != Z_OK || togo <= newsize){
377 CreateStandartPackets(buffer, togo, currentblock);
378 return;
381 CMemFile memfile(output.get(), newsize);
383 uint32 totalPayloadSize = 0;
384 uint32 oldSize = togo;
385 togo = newsize;
386 uint32 nPacketSize;
387 if (togo > 10240) {
388 nPacketSize = togo/(uint32)(togo/10240);
389 } else {
390 nPacketSize = togo;
393 while (togo) {
394 if (togo < nPacketSize*2) {
395 nPacketSize = togo;
397 togo -= nPacketSize;
399 bool isLargeBlock = (currentblock->StartOffset > 0xFFFFFFFF) || (currentblock->EndOffset > 0xFFFFFFFF);
401 CMemFile data(nPacketSize + 16 + (isLargeBlock ? 12 : 8));
402 data.WriteHash(GetUploadFileID());
403 if (isLargeBlock) {
404 data.WriteUInt64(currentblock->StartOffset);
405 } else {
406 data.WriteUInt32(currentblock->StartOffset);
408 data.WriteUInt32(newsize);
409 char *tempbuf = new char[nPacketSize];
410 memfile.Read(tempbuf, nPacketSize);
411 data.Write(tempbuf,nPacketSize);
412 delete [] tempbuf;
413 CPacket* packet = new CPacket(data, OP_EMULEPROT, (isLargeBlock ? OP_COMPRESSEDPART_I64 : OP_COMPRESSEDPART));
415 // approximate payload size
416 uint32 payloadSize = nPacketSize*oldSize/newsize;
418 if (togo == 0 && totalPayloadSize+payloadSize < oldSize) {
419 payloadSize = oldSize-totalPayloadSize;
422 totalPayloadSize += payloadSize;
424 // put packet directly on socket
425 theStats::AddUpOverheadFileRequest(24);
426 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize);
427 AddDebugLogLineM( false, logLocalClient, wxString::Format(wxT("Local Client: %s to "), (isLargeBlock ? wxT("OP_COMPRESSEDPART_I64") : wxT("OP_COMPRESSEDPART"))) + GetFullIP() );
428 m_socket->SendPacket(packet,true,false, payloadSize);
433 void CUpDownClient::ProcessExtendedInfo(const CMemFile *data, CKnownFile *tempreqfile)
435 m_uploadingfile->UpdateUpPartsFrequency( this, false ); // Decrement
436 m_upPartStatus.clear();
437 m_nUpCompleteSourcesCount= 0;
439 if( GetExtendedRequestsVersion() == 0 ) {
440 // Something is coded wrong on this client if he's sending something it doesn't advertise.
441 return;
444 if (data->GetLength() == 16) {
445 // Wrong again. Advertised >0 but send a 0-type packet.
446 // But this time we'll disconnect it.
447 throw CInvalidPacket(wxT("Wrong size on extended info packet"));
450 uint16 nED2KUpPartCount = data->ReadUInt16();
451 if (!nED2KUpPartCount) {
452 m_upPartStatus.resize( tempreqfile->GetPartCount(), 0 );
453 } else {
454 if (tempreqfile->GetED2KPartCount() != nED2KUpPartCount) {
455 // We already checked if we are talking about the same file.. So if we get here, something really strange happened!
456 m_upPartStatus.clear();
457 return;
460 m_upPartStatus.resize( tempreqfile->GetPartCount(), 0 );
462 try {
463 uint16 done = 0;
464 while (done != m_upPartStatus.size()) {
465 uint8 toread = data->ReadUInt8();
466 for (sint32 i = 0;i != 8;i++){
467 m_upPartStatus[done] = (toread>>i)&1;
468 // We may want to use this for another feature..
469 // if (m_upPartStatus[done] && !tempreqfile->IsComplete(done*PARTSIZE,((done+1)*PARTSIZE)-1))
470 // bPartsNeeded = true;
471 done++;
472 if (done == m_upPartStatus.size()) {
473 break;
477 } catch (...) {
478 // We want the increment the frequency even if we didn't read everything
479 m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment
481 throw;
484 if (GetExtendedRequestsVersion() > 1) {
485 uint16 nCompleteCountLast = GetUpCompleteSourcesCount();
486 uint16 nCompleteCountNew = data->ReadUInt16();
487 SetUpCompleteSourcesCount(nCompleteCountNew);
488 if (nCompleteCountLast != nCompleteCountNew) {
489 tempreqfile->UpdatePartsInfo();
494 m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment
496 Notify_QlistRefreshClient(this);
500 void CUpDownClient::SetUploadFileID(CKnownFile* newreqfile)
502 if (m_uploadingfile == newreqfile) {
503 return;
504 } else if (m_uploadingfile) {
505 m_uploadingfile->RemoveUploadingClient(this);
506 m_uploadingfile->UpdateUpPartsFrequency(this, false); // Decrement
509 if (newreqfile) {
510 // This is a new file! update info
511 newreqfile->AddUploadingClient(this);
513 if (m_requpfileid != newreqfile->GetFileHash()) {
514 m_requpfileid = newreqfile->GetFileHash();
515 m_upPartStatus.clear();
516 m_upPartStatus.resize( newreqfile->GetPartCount(), 0 );
517 } else {
518 // this is the same file we already had assigned. Only update data.
519 newreqfile->UpdateUpPartsFrequency(this, true); // Increment
522 m_uploadingfile = newreqfile;
523 } else {
524 m_upPartStatus.clear();
525 m_nUpCompleteSourcesCount = 0;
526 // This clears m_uploadingfile and m_requpfileid
527 ClearUploadFileID();
532 void CUpDownClient::AddReqBlock(Requested_Block_Struct* reqblock)
534 if (GetUploadState() != US_UPLOADING) {
535 AddDebugLogLineM(false, logRemoteClient, wxT("UploadClient: Client tried to add requested block when not in upload slot! Prevented requested blocks from being added."));
536 delete reqblock;
537 return;
541 std::list<Requested_Block_Struct*>::iterator it = m_DoneBlocks_list.begin();
542 for (; it != m_DoneBlocks_list.end(); ++it) {
543 if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) {
544 delete reqblock;
545 return;
551 std::list<Requested_Block_Struct*>::iterator it = m_BlockRequests_queue.begin();
552 for (; it != m_BlockRequests_queue.end(); ++it) {
553 if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) {
554 delete reqblock;
555 return;
560 m_BlockRequests_queue.push_back(reqblock);
564 uint32 CUpDownClient::GetWaitStartTime() const
566 uint32 dwResult = 0;
568 if ( credits ) {
569 dwResult = credits->GetSecureWaitStartTime(GetIP());
571 if (dwResult > m_dwUploadTime && IsDownloading()) {
572 // This happens only if two clients with invalid securehash are in the queue - if at all
573 dwResult = m_dwUploadTime - 1;
577 return dwResult;
581 void CUpDownClient::SetWaitStartTime()
583 if ( credits ) {
584 credits->SetSecWaitStartTime(GetIP());
589 void CUpDownClient::ClearWaitStartTime()
591 if ( credits ) {
592 credits->ClearWaitStartTime();
597 uint32 CUpDownClient::SendBlockData()
599 uint32 curTick = ::GetTickCount();
600 uint64 sentBytesCompleteFile = 0;
601 uint64 sentBytesPartFile = 0;
602 uint64 sentBytesPayload = 0;
604 if (m_socket) {
605 CEMSocket* s = m_socket;
606 // uint32 uUpStatsPort = GetUserPort();
608 // Extended statistics information based on which client software and which port we sent this data to...
609 // This also updates the grand total for sent bytes, etc. And where this data came from.
610 sentBytesCompleteFile = s->GetSentBytesCompleteFileSinceLastCallAndReset();
611 sentBytesPartFile = s->GetSentBytesPartFileSinceLastCallAndReset();
612 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, false, true, sentBytesCompleteFile, (IsFriend() && GetFriendSlot()));
613 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, true, true, sentBytesPartFile, (IsFriend() && GetFriendSlot()));
615 m_nTransferredUp += sentBytesCompleteFile + sentBytesPartFile;
616 credits->AddUploaded(sentBytesCompleteFile + sentBytesPartFile, GetIP(), theApp->CryptoAvailable());
618 sentBytesPayload = s->GetSentPayloadSinceLastCallAndReset();
619 m_nCurQueueSessionPayloadUp += sentBytesPayload;
621 if (theApp->uploadqueue->CheckForTimeOver(this)) {
622 theApp->uploadqueue->RemoveFromUploadQueue(this, true);
623 SendOutOfPartReqsAndAddToWaitingQueue();
624 } else {
625 // read blocks from file and put on socket
626 CreateNextBlockPackage();
630 if(sentBytesCompleteFile + sentBytesPartFile > 0 ||
631 m_AvarageUDR_list.empty() || (curTick - m_AvarageUDR_list.back().timestamp) > 1*1000) {
632 // Store how much data we've transferred this round,
633 // to be able to calculate average speed later
634 // keep sum of all values in list up to date
635 TransferredData newitem = {sentBytesCompleteFile + sentBytesPartFile, curTick};
636 m_AvarageUDR_list.push_back(newitem);
637 m_nSumForAvgUpDataRate += sentBytesCompleteFile + sentBytesPartFile;
640 // remove to old values in list
641 while ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 10*1000) {
642 // keep sum of all values in list up to date
643 m_nSumForAvgUpDataRate -= m_AvarageUDR_list.front().datalen;
644 m_AvarageUDR_list.pop_front();
647 // Calculate average speed for this slot
648 if ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 0 && GetUpStartTimeDelay() > 2*1000) {
649 m_nUpDatarate = ((uint64)m_nSumForAvgUpDataRate*1000) / (curTick-m_AvarageUDR_list.front().timestamp);
650 } else {
651 // not enough values to calculate trustworthy speed. Use -1 to tell this
652 m_nUpDatarate = 0; //-1;
655 // Check if it's time to update the display.
656 m_cSendblock++;
657 if (m_cSendblock == 30){
658 m_cSendblock = 0;
659 Notify_UploadCtrlRefreshClient(this);
662 return sentBytesCompleteFile + sentBytesPartFile;
666 void CUpDownClient::SendOutOfPartReqsAndAddToWaitingQueue()
668 // Kry - this is actually taken from eMule, but makes a lot of sense ;)
670 //OP_OUTOFPARTREQS will tell the downloading client to go back to OnQueue..
671 //The main reason for this is that if we put the client back on queue and it goes
672 //back to the upload before the socket times out... We get a situation where the
673 //downloader thinks it already sent the requested blocks and the uploader thinks
674 //the downloader didn't send any request blocks. Then the connection times out..
675 //I did some tests with eDonkey also and it seems to work well with them also..
677 // Send this inmediately, don't queue.
678 CPacket* pPacket = new CPacket(OP_OUTOFPARTREQS, 0, OP_EDONKEYPROT);
679 theStats::AddUpOverheadFileRequest(pPacket->GetPacketSize());
680 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_OUTOFPARTREQS to ") + GetFullIP() );
681 SendPacket(pPacket, true, true);
683 theApp->uploadqueue->AddClientToQueue(this);
688 * See description for CEMSocket::TruncateQueues().
690 void CUpDownClient::FlushSendBlocks()
692 // Call this when you stop upload, or the socket might be not able to send
693 if (m_socket) { //socket may be NULL...
694 m_socket->TruncateQueues();
699 void CUpDownClient::SendHashsetPacket(const CMD4Hash& forfileid)
701 CKnownFile* file = theApp->sharedfiles->GetFileByID( forfileid );
702 bool from_dq = false;
703 if ( !file ) {
704 from_dq = true;
705 if ((file = theApp->downloadqueue->GetFileByID(forfileid)) == NULL) {
706 AddLogLineM(false, CFormat( _("Hashset requested for unknown file: %s") ) % forfileid.Encode() );
708 return;
712 if ( !file->GetHashCount() ) {
713 if (from_dq) {
714 AddDebugLogLineM(false, logRemoteClient, wxT("Requested hashset could not be found"));
715 return;
716 } else {
717 file = theApp->downloadqueue->GetFileByID(forfileid);
718 if (!(file && file->GetHashCount())) {
719 AddDebugLogLineM(false, logRemoteClient, wxT("Requested hashset could not be found"));
720 return;
725 CMemFile data(1024);
726 data.WriteHash(file->GetFileHash());
727 uint16 parts = file->GetHashCount();
728 data.WriteUInt16(parts);
729 for (int i = 0; i != parts; i++) {
730 data.WriteHash(file->GetPartHash(i));
732 CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_HASHSETANSWER);
733 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
734 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_HASHSETANSWER to ") + GetFullIP());
735 SendPacket(packet,true,true);
739 void CUpDownClient::ClearUploadBlockRequests()
741 FlushSendBlocks();
742 DeleteContents(m_BlockRequests_queue);
743 DeleteContents(m_DoneBlocks_list);
747 void CUpDownClient::SendRankingInfo(){
748 if (!ExtProtocolAvailable()) {
749 return;
752 uint16 nRank = theApp->uploadqueue->GetWaitingPosition(this);
753 if (!nRank) {
754 return;
757 CMemFile data;
758 data.WriteUInt16(nRank);
759 // Kry: what are these zero bytes for. are they really correct?
760 // Kry - Well, eMule does like that. I guess they're ok.
761 data.WriteUInt32(0); data.WriteUInt32(0); data.WriteUInt16(0);
762 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_QUEUERANKING);
763 theStats::AddUpOverheadOther(packet->GetPacketSize());
764 AddDebugLogLineM(false, logLocalClient, wxT("Local Client: OP_QUEUERANKING to ") + GetFullIP());
765 SendPacket(packet,true,true);
769 void CUpDownClient::SendCommentInfo(CKnownFile* file)
771 if (!m_bCommentDirty || file == NULL || !ExtProtocolAvailable() || m_byAcceptCommentVer < 1) {
772 return;
774 m_bCommentDirty = false;
776 // Truncate to max len.
777 wxString desc = file->GetFileComment().Left(MAXFILECOMMENTLEN);
778 uint8 rating = file->GetFileRating();
780 if ( file->GetFileRating() == 0 && desc.IsEmpty() ) {
781 return;
784 CMemFile data(256);
785 data.WriteUInt8(rating);
786 data.WriteString(desc, GetUnicodeSupport(), 4 /* size it's uint32 */);
788 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_FILEDESC);
789 theStats::AddUpOverheadOther(packet->GetPacketSize());
790 AddDebugLogLineM(false, logLocalClient, wxT("Local Client: OP_FILEDESC to ") + GetFullIP());
791 SendPacket(packet,true);
794 void CUpDownClient::UnBan(){
795 m_Aggressiveness = 0;
797 theApp->clientlist->AddTrackClient(this);
798 theApp->clientlist->RemoveBannedClient( GetIP() );
799 SetUploadState(US_NONE);
800 ClearWaitStartTime();
802 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
805 void CUpDownClient::Ban(){
806 theApp->clientlist->AddTrackClient(this);
807 theApp->clientlist->AddBannedClient( GetIP() );
809 AddDebugLogLineM( false, logClient, wxT("Client '") + GetUserName() + wxT("' seems to be an aggressive client and is banned from the uploadqueue"));
811 SetUploadState(US_BANNED);
813 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
814 Notify_QlistRefreshClient(this);
817 bool CUpDownClient::IsBanned() const
819 return ( (theApp->clientlist->IsBannedClient(GetIP()) ) && m_nDownloadState != DS_DOWNLOADING);
822 void CUpDownClient::CheckForAggressive()
824 uint32 cur_time = ::GetTickCount();
826 // First call, initalize
827 if ( !m_LastFileRequest ) {
828 m_LastFileRequest = cur_time;
829 return;
832 // Is this an aggressive request?
833 if ( ( cur_time - m_LastFileRequest ) < MIN_REQUESTTIME ) {
834 m_Aggressiveness += 3;
836 // Is the client EVIL?
837 if ( m_Aggressiveness >= 10 && (!IsBanned() && m_nDownloadState != DS_DOWNLOADING )) {
838 AddDebugLogLineM( false, logClient, CFormat( wxT("Aggressive client banned (score: %d): %s -- %s -- %s") )
839 % m_Aggressiveness
840 % m_Username
841 % m_strModVersion
842 % m_fullClientVerString );
843 Ban();
845 } else {
846 // Polite request, reward client
847 if ( m_Aggressiveness )
848 m_Aggressiveness--;
851 m_LastFileRequest = cur_time;
855 void CUpDownClient::SetUploadFileID(const CMD4Hash& new_id)
857 // Update the uploading file found
858 CKnownFile* uploadingfile = theApp->sharedfiles->GetFileByID(new_id);
859 if ( !uploadingfile ) {
860 // Can this really happen?
861 uploadingfile = theApp->downloadqueue->GetFileByID(new_id);
863 SetUploadFileID(uploadingfile); // This will update queue count on old and new file.
866 void CUpDownClient::ProcessRequestPartsPacket(const byte* pachPacket, uint32 nSize, bool largeblocks) {
868 CMemFile data(pachPacket, nSize);
870 CMD4Hash reqfilehash = data.ReadHash();
872 uint64 auStartOffsets[3];
873 uint64 auEndOffsets[3];
875 if (largeblocks) {
876 auStartOffsets[0] = data.ReadUInt64();
877 auStartOffsets[1] = data.ReadUInt64();
878 auStartOffsets[2] = data.ReadUInt64();
880 auEndOffsets[0] = data.ReadUInt64();
881 auEndOffsets[1] = data.ReadUInt64();
882 auEndOffsets[2] = data.ReadUInt64();
883 } else {
884 auStartOffsets[0] = data.ReadUInt32();
885 auStartOffsets[1] = data.ReadUInt32();
886 auStartOffsets[2] = data.ReadUInt32();
888 auEndOffsets[0] = data.ReadUInt32();
889 auEndOffsets[1] = data.ReadUInt32();
890 auEndOffsets[2] = data.ReadUInt32();
893 for (unsigned int i = 0; i < itemsof(auStartOffsets); i++) {
894 AddDebugLogLineM(false, logClient,
895 wxString::Format(wxT("Client requests %u"), i)
896 + wxT(" ") + wxString::Format(wxT("File block %u-%u (%d bytes):"), auStartOffsets[i], auEndOffsets[i], auEndOffsets[i] - auStartOffsets[i])
897 + wxT(" ") + GetFullIP());
898 if (auEndOffsets[i] > auStartOffsets[i]) {
899 Requested_Block_Struct* reqblock = new Requested_Block_Struct;
900 reqblock->StartOffset = auStartOffsets[i];
901 reqblock->EndOffset = auEndOffsets[i];
902 md4cpy(reqblock->FileID, reqfilehash.GetHash());
903 reqblock->transferred = 0;
904 AddReqBlock(reqblock);
905 } else {
906 if (auEndOffsets[i] != 0 || auStartOffsets[i] != 0) {
907 AddDebugLogLineM(false, logClient, wxT("Client request is invalid!"));
913 void CUpDownClient::ProcessRequestPartsPacketv2(const CMemFile& data) {
915 CMD4Hash reqfilehash = data.ReadHash();
917 uint8 numblocks = data.ReadUInt8();
919 for (int i = 0; i < numblocks; i++) {
920 Requested_Block_Struct* reqblock = new Requested_Block_Struct;
921 try {
922 reqblock->StartOffset = data.GetIntTagValue();
923 // We have to do +1, because the block matching uses that.
924 reqblock->EndOffset = data.GetIntTagValue() + 1;
925 if ((reqblock->StartOffset || reqblock->EndOffset) && (reqblock->StartOffset > reqblock->EndOffset)) {
926 AddDebugLogLineM(false, logClient, wxString::Format(wxT("Client request is invalid! %i / %i"),reqblock->StartOffset, reqblock->EndOffset));
927 throw wxString(wxT("Client request is invalid!"));
930 AddDebugLogLineM(false, logClient,
931 wxString::Format(wxT("Client requests %u"), i)
932 + wxT(" ") + wxString::Format(wxT("File block %u-%u (%d bytes):"),reqblock->StartOffset, reqblock->EndOffset, reqblock->EndOffset - reqblock->StartOffset)
933 += wxT(" ") + GetFullIP());
935 md4cpy(reqblock->FileID, reqfilehash.GetHash());
936 reqblock->transferred = 0;
937 AddReqBlock(reqblock);
938 } catch (...) {
939 delete reqblock;
940 throw;
944 // File_checked_for_headers