Upstream tarball 9407
[amule.git] / src / UploadClient.cpp
blob2bc7f3487501a771d0b5278400afb7dba1d0c702
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->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 CFileArea area;
265 if (!srcfile->IsPartFile()){
266 CFile file;
267 if ( !file.Open(fullname, CFile::read) ) {
268 // The file was most likely moved/deleted. However it is likely that the
269 // same is true for other files, so we recheck all shared files.
270 AddLogLineM( false, CFormat( _("Failed to open file (%s), removing from list of shared files.") ) % srcfile->GetFileName() );
271 theApp->sharedfiles->RemoveFile(srcfile);
273 throw wxString(wxT("Failed to open requested file: Removing from list of shared files!"));
275 area.Read(file, togo);
276 } else {
277 if (!((CPartFile*)srcfile)->ReadData(area, currentblock->StartOffset, togo))
278 throw wxString(wxT("Failed to read from requested partfile"));
281 //#warning Part of the above import.
282 #if 0
283 if (lockFile.m_pObject){
284 lockFile.m_pObject->Unlock(); // Unlock the (part) file as soon as we are done with accessing it.
285 lockFile.m_pObject = NULL;
287 #endif
289 SetUploadFileID(srcfile);
291 // check extention to decide whether to compress or not
292 if (m_byDataCompVer == 1 && GetFiletype(srcfile->GetFileName()) != ftArchive) {
293 CreatePackedPackets(area.GetBuffer(), togo, currentblock);
294 } else {
295 CreateStandartPackets(area.GetBuffer(), togo, currentblock);
298 // file statistic
299 srcfile->statistic.AddTransferred(togo);
301 m_addedPayloadQueueSession += togo;
303 Requested_Block_Struct* block = m_BlockRequests_queue.front();
305 m_BlockRequests_queue.pop_front();
306 m_DoneBlocks_list.push_front(block);
309 return;
310 } catch (const wxString& error) {
311 AddDebugLogLineM(false, logClient, wxT("Client '") + GetUserName() + wxT("' caused error while creating packet (") + error + wxT(") - disconnecting client"));
312 } catch (const CIOFailureException& error) {
313 AddDebugLogLineM(true, logClient, wxT("IO failure while reading requested file: ") + error.what());
314 } catch (const CEOFException& WXUNUSED(error)) {
315 AddDebugLogLineM(true, logClient, GetClientFullInfo() + wxT(" requested file-data at an invalid position - disconnecting"));
318 // Error occured.
319 theApp->uploadqueue->RemoveFromUploadQueue(this);
323 void CUpDownClient::CreateStandartPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock)
325 uint32 nPacketSize;
327 CMemFile memfile(buffer, togo);
328 if (togo > 10240) {
329 nPacketSize = togo/(uint32)(togo/10240);
330 } else {
331 nPacketSize = togo;
334 while (togo){
335 if (togo < nPacketSize*2) {
336 nPacketSize = togo;
339 wxASSERT(nPacketSize);
340 togo -= nPacketSize;
342 uint64 endpos = (currentblock->EndOffset - togo);
343 uint64 startpos = endpos - nPacketSize;
345 bool bLargeBlocks = (startpos > 0xFFFFFFFF) || (endpos > 0xFFFFFFFF);
347 CMemFile data(nPacketSize + 16 + 2 * (bLargeBlocks ? 8 :4));
348 data.WriteHash(GetUploadFileID());
349 if (bLargeBlocks) {
350 data.WriteUInt64(startpos);
351 data.WriteUInt64(endpos);
352 } else {
353 data.WriteUInt32(startpos);
354 data.WriteUInt32(endpos);
356 char *tempbuf = new char[nPacketSize];
357 memfile.Read(tempbuf, nPacketSize);
358 data.Write(tempbuf, nPacketSize);
359 delete [] tempbuf;
360 CPacket* packet = new CPacket(data, (bLargeBlocks ? OP_EMULEPROT : OP_EDONKEYPROT), (bLargeBlocks ? (uint8)OP_SENDINGPART_I64 : (uint8)OP_SENDINGPART));
361 theStats::AddUpOverheadFileRequest(16 + 2 * (bLargeBlocks ? 8 :4));
362 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize);
363 AddDebugLogLineM( false, logLocalClient, wxString::Format(wxT("Local Client: %s to "),(bLargeBlocks ? wxT("OP_SENDINGPART_I64") : wxT("OP_SENDINGPART"))) + GetFullIP() );
364 m_socket->SendPacket(packet,true,false, nPacketSize);
369 void CUpDownClient::CreatePackedPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock)
371 uLongf newsize = togo+300;
372 CScopedArray<byte> output(newsize);
373 uint16 result = compress2(output.get(), &newsize, buffer, togo, 9);
374 if (result != Z_OK || togo <= newsize){
375 CreateStandartPackets(buffer, togo, currentblock);
376 return;
379 CMemFile memfile(output.get(), newsize);
381 uint32 totalPayloadSize = 0;
382 uint32 oldSize = togo;
383 togo = newsize;
384 uint32 nPacketSize;
385 if (togo > 10240) {
386 nPacketSize = togo/(uint32)(togo/10240);
387 } else {
388 nPacketSize = togo;
391 while (togo) {
392 if (togo < nPacketSize*2) {
393 nPacketSize = togo;
395 togo -= nPacketSize;
397 bool isLargeBlock = (currentblock->StartOffset > 0xFFFFFFFF) || (currentblock->EndOffset > 0xFFFFFFFF);
399 CMemFile data(nPacketSize + 16 + (isLargeBlock ? 12 : 8));
400 data.WriteHash(GetUploadFileID());
401 if (isLargeBlock) {
402 data.WriteUInt64(currentblock->StartOffset);
403 } else {
404 data.WriteUInt32(currentblock->StartOffset);
406 data.WriteUInt32(newsize);
407 char *tempbuf = new char[nPacketSize];
408 memfile.Read(tempbuf, nPacketSize);
409 data.Write(tempbuf,nPacketSize);
410 delete [] tempbuf;
411 CPacket* packet = new CPacket(data, OP_EMULEPROT, (isLargeBlock ? OP_COMPRESSEDPART_I64 : OP_COMPRESSEDPART));
413 // approximate payload size
414 uint32 payloadSize = nPacketSize*oldSize/newsize;
416 if (togo == 0 && totalPayloadSize+payloadSize < oldSize) {
417 payloadSize = oldSize-totalPayloadSize;
420 totalPayloadSize += payloadSize;
422 // put packet directly on socket
423 theStats::AddUpOverheadFileRequest(24);
424 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize);
425 AddDebugLogLineM( false, logLocalClient, wxString::Format(wxT("Local Client: %s to "), (isLargeBlock ? wxT("OP_COMPRESSEDPART_I64") : wxT("OP_COMPRESSEDPART"))) + GetFullIP() );
426 m_socket->SendPacket(packet,true,false, payloadSize);
431 void CUpDownClient::ProcessExtendedInfo(const CMemFile *data, CKnownFile *tempreqfile)
433 m_uploadingfile->UpdateUpPartsFrequency( this, false ); // Decrement
434 m_upPartStatus.clear();
435 m_nUpCompleteSourcesCount= 0;
437 if( GetExtendedRequestsVersion() == 0 ) {
438 // Something is coded wrong on this client if he's sending something it doesn't advertise.
439 return;
442 if (data->GetLength() == 16) {
443 // Wrong again. Advertised >0 but send a 0-type packet.
444 // But this time we'll disconnect it.
445 throw CInvalidPacket(wxT("Wrong size on extended info packet"));
448 uint16 nED2KUpPartCount = data->ReadUInt16();
449 if (!nED2KUpPartCount) {
450 m_upPartStatus.resize( tempreqfile->GetPartCount(), 0 );
451 } else {
452 if (tempreqfile->GetED2KPartCount() != nED2KUpPartCount) {
453 // We already checked if we are talking about the same file.. So if we get here, something really strange happened!
454 m_upPartStatus.clear();
455 return;
458 m_upPartStatus.resize( tempreqfile->GetPartCount(), 0 );
460 try {
461 uint16 done = 0;
462 while (done != m_upPartStatus.size()) {
463 uint8 toread = data->ReadUInt8();
464 for (sint32 i = 0;i != 8;i++){
465 m_upPartStatus[done] = (toread>>i)&1;
466 // We may want to use this for another feature..
467 // if (m_upPartStatus[done] && !tempreqfile->IsComplete(done*PARTSIZE,((done+1)*PARTSIZE)-1))
468 // bPartsNeeded = true;
469 done++;
470 if (done == m_upPartStatus.size()) {
471 break;
475 } catch (...) {
476 // We want the increment the frequency even if we didn't read everything
477 m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment
479 throw;
482 if (GetExtendedRequestsVersion() > 1) {
483 uint16 nCompleteCountLast = GetUpCompleteSourcesCount();
484 uint16 nCompleteCountNew = data->ReadUInt16();
485 SetUpCompleteSourcesCount(nCompleteCountNew);
486 if (nCompleteCountLast != nCompleteCountNew) {
487 tempreqfile->UpdatePartsInfo();
492 m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment
494 Notify_QlistRefreshClient(this);
498 void CUpDownClient::SetUploadFileID(CKnownFile* newreqfile)
500 if (m_uploadingfile == newreqfile) {
501 return;
502 } else if (m_uploadingfile) {
503 m_uploadingfile->RemoveUploadingClient(this);
504 m_uploadingfile->UpdateUpPartsFrequency(this, false); // Decrement
507 if (newreqfile) {
508 // This is a new file! update info
509 newreqfile->AddUploadingClient(this);
511 if (m_requpfileid != newreqfile->GetFileHash()) {
512 m_requpfileid = newreqfile->GetFileHash();
513 m_upPartStatus.clear();
514 m_upPartStatus.resize( newreqfile->GetPartCount(), 0 );
515 } else {
516 // this is the same file we already had assigned. Only update data.
517 newreqfile->UpdateUpPartsFrequency(this, true); // Increment
520 m_uploadingfile = newreqfile;
521 } else {
522 m_upPartStatus.clear();
523 m_nUpCompleteSourcesCount = 0;
524 // This clears m_uploadingfile and m_requpfileid
525 ClearUploadFileID();
530 void CUpDownClient::AddReqBlock(Requested_Block_Struct* reqblock)
532 if (GetUploadState() != US_UPLOADING) {
533 AddDebugLogLineM(false, logRemoteClient, wxT("UploadClient: Client tried to add requested block when not in upload slot! Prevented requested blocks from being added."));
534 delete reqblock;
535 return;
539 std::list<Requested_Block_Struct*>::iterator it = m_DoneBlocks_list.begin();
540 for (; it != m_DoneBlocks_list.end(); ++it) {
541 if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) {
542 delete reqblock;
543 return;
549 std::list<Requested_Block_Struct*>::iterator it = m_BlockRequests_queue.begin();
550 for (; it != m_BlockRequests_queue.end(); ++it) {
551 if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) {
552 delete reqblock;
553 return;
558 m_BlockRequests_queue.push_back(reqblock);
562 uint32 CUpDownClient::GetWaitStartTime() const
564 uint32 dwResult = 0;
566 if ( credits ) {
567 dwResult = credits->GetSecureWaitStartTime(GetIP());
569 if (dwResult > m_dwUploadTime && IsDownloading()) {
570 // This happens only if two clients with invalid securehash are in the queue - if at all
571 dwResult = m_dwUploadTime - 1;
575 return dwResult;
579 void CUpDownClient::SetWaitStartTime()
581 if ( credits ) {
582 credits->SetSecWaitStartTime(GetIP());
587 void CUpDownClient::ClearWaitStartTime()
589 if ( credits ) {
590 credits->ClearWaitStartTime();
595 uint32 CUpDownClient::SendBlockData()
597 uint32 curTick = ::GetTickCount();
598 uint64 sentBytesCompleteFile = 0;
599 uint64 sentBytesPartFile = 0;
600 uint64 sentBytesPayload = 0;
602 if (m_socket) {
603 CEMSocket* s = m_socket;
604 // uint32 uUpStatsPort = GetUserPort();
606 // Extended statistics information based on which client software and which port we sent this data to...
607 // This also updates the grand total for sent bytes, etc. And where this data came from.
608 sentBytesCompleteFile = s->GetSentBytesCompleteFileSinceLastCallAndReset();
609 sentBytesPartFile = s->GetSentBytesPartFileSinceLastCallAndReset();
610 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, false, true, sentBytesCompleteFile, (IsFriend() && GetFriendSlot()));
611 // thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, true, true, sentBytesPartFile, (IsFriend() && GetFriendSlot()));
613 m_nTransferredUp += sentBytesCompleteFile + sentBytesPartFile;
614 credits->AddUploaded(sentBytesCompleteFile + sentBytesPartFile, GetIP(), theApp->CryptoAvailable());
616 sentBytesPayload = s->GetSentPayloadSinceLastCallAndReset();
617 m_nCurQueueSessionPayloadUp += sentBytesPayload;
619 if (theApp->uploadqueue->CheckForTimeOver(this)) {
620 theApp->uploadqueue->RemoveFromUploadQueue(this, true);
621 SendOutOfPartReqsAndAddToWaitingQueue();
622 } else {
623 // read blocks from file and put on socket
624 CreateNextBlockPackage();
628 if(sentBytesCompleteFile + sentBytesPartFile > 0 ||
629 m_AvarageUDR_list.empty() || (curTick - m_AvarageUDR_list.back().timestamp) > 1*1000) {
630 // Store how much data we've transferred this round,
631 // to be able to calculate average speed later
632 // keep sum of all values in list up to date
633 TransferredData newitem = {sentBytesCompleteFile + sentBytesPartFile, curTick};
634 m_AvarageUDR_list.push_back(newitem);
635 m_nSumForAvgUpDataRate += sentBytesCompleteFile + sentBytesPartFile;
638 // remove to old values in list
639 while ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 10*1000) {
640 // keep sum of all values in list up to date
641 m_nSumForAvgUpDataRate -= m_AvarageUDR_list.front().datalen;
642 m_AvarageUDR_list.pop_front();
645 // Calculate average speed for this slot
646 if ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 0 && GetUpStartTimeDelay() > 2*1000) {
647 m_nUpDatarate = ((uint64)m_nSumForAvgUpDataRate*1000) / (curTick-m_AvarageUDR_list.front().timestamp);
648 } else {
649 // not enough values to calculate trustworthy speed. Use -1 to tell this
650 m_nUpDatarate = 0; //-1;
653 // Check if it's time to update the display.
654 m_cSendblock++;
655 if (m_cSendblock == 30){
656 m_cSendblock = 0;
657 Notify_UploadCtrlRefreshClient(this);
660 return sentBytesCompleteFile + sentBytesPartFile;
664 void CUpDownClient::SendOutOfPartReqsAndAddToWaitingQueue()
666 // Kry - this is actually taken from eMule, but makes a lot of sense ;)
668 //OP_OUTOFPARTREQS will tell the downloading client to go back to OnQueue..
669 //The main reason for this is that if we put the client back on queue and it goes
670 //back to the upload before the socket times out... We get a situation where the
671 //downloader thinks it already sent the requested blocks and the uploader thinks
672 //the downloader didn't send any request blocks. Then the connection times out..
673 //I did some tests with eDonkey also and it seems to work well with them also..
675 // Send this inmediately, don't queue.
676 CPacket* pPacket = new CPacket(OP_OUTOFPARTREQS, 0, OP_EDONKEYPROT);
677 theStats::AddUpOverheadFileRequest(pPacket->GetPacketSize());
678 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_OUTOFPARTREQS to ") + GetFullIP() );
679 SendPacket(pPacket, true, true);
681 theApp->uploadqueue->AddClientToQueue(this);
686 * See description for CEMSocket::TruncateQueues().
688 void CUpDownClient::FlushSendBlocks()
690 // Call this when you stop upload, or the socket might be not able to send
691 if (m_socket) { //socket may be NULL...
692 m_socket->TruncateQueues();
697 void CUpDownClient::SendHashsetPacket(const CMD4Hash& forfileid)
699 CKnownFile* file = theApp->sharedfiles->GetFileByID( forfileid );
700 bool from_dq = false;
701 if ( !file ) {
702 from_dq = true;
703 if ((file = theApp->downloadqueue->GetFileByID(forfileid)) == NULL) {
704 AddLogLineM(false, CFormat( _("Hashset requested for unknown file: %s") ) % forfileid.Encode() );
706 return;
710 if ( !file->GetHashCount() ) {
711 if (from_dq) {
712 AddDebugLogLineM(false, logRemoteClient, wxT("Requested hashset could not be found"));
713 return;
714 } else {
715 file = theApp->downloadqueue->GetFileByID(forfileid);
716 if (!(file && file->GetHashCount())) {
717 AddDebugLogLineM(false, logRemoteClient, wxT("Requested hashset could not be found"));
718 return;
723 CMemFile data(1024);
724 data.WriteHash(file->GetFileHash());
725 uint16 parts = file->GetHashCount();
726 data.WriteUInt16(parts);
727 for (int i = 0; i != parts; i++) {
728 data.WriteHash(file->GetPartHash(i));
730 CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_HASHSETANSWER);
731 theStats::AddUpOverheadFileRequest(packet->GetPacketSize());
732 AddDebugLogLineM( false, logLocalClient, wxT("Local Client: OP_HASHSETANSWER to ") + GetFullIP());
733 SendPacket(packet,true,true);
737 void CUpDownClient::ClearUploadBlockRequests()
739 FlushSendBlocks();
740 DeleteContents(m_BlockRequests_queue);
741 DeleteContents(m_DoneBlocks_list);
745 void CUpDownClient::SendRankingInfo(){
746 if (!ExtProtocolAvailable()) {
747 return;
750 uint16 nRank = theApp->uploadqueue->GetWaitingPosition(this);
751 if (!nRank) {
752 return;
755 CMemFile data;
756 data.WriteUInt16(nRank);
757 // Kry: what are these zero bytes for. are they really correct?
758 // Kry - Well, eMule does like that. I guess they're ok.
759 data.WriteUInt32(0); data.WriteUInt32(0); data.WriteUInt16(0);
760 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_QUEUERANKING);
761 theStats::AddUpOverheadOther(packet->GetPacketSize());
762 AddDebugLogLineM(false, logLocalClient, wxT("Local Client: OP_QUEUERANKING to ") + GetFullIP());
763 SendPacket(packet,true,true);
767 void CUpDownClient::SendCommentInfo(CKnownFile* file)
769 if (!m_bCommentDirty || file == NULL || !ExtProtocolAvailable() || m_byAcceptCommentVer < 1) {
770 return;
772 m_bCommentDirty = false;
774 // Truncate to max len.
775 wxString desc = file->GetFileComment().Left(MAXFILECOMMENTLEN);
776 uint8 rating = file->GetFileRating();
778 if ( file->GetFileRating() == 0 && desc.IsEmpty() ) {
779 return;
782 CMemFile data(256);
783 data.WriteUInt8(rating);
784 data.WriteString(desc, GetUnicodeSupport(), 4 /* size it's uint32 */);
786 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_FILEDESC);
787 theStats::AddUpOverheadOther(packet->GetPacketSize());
788 AddDebugLogLineM(false, logLocalClient, wxT("Local Client: OP_FILEDESC to ") + GetFullIP());
789 SendPacket(packet,true);
792 void CUpDownClient::UnBan(){
793 m_Aggressiveness = 0;
795 theApp->clientlist->AddTrackClient(this);
796 theApp->clientlist->RemoveBannedClient( GetIP() );
797 SetUploadState(US_NONE);
798 ClearWaitStartTime();
800 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
803 void CUpDownClient::Ban(){
804 theApp->clientlist->AddTrackClient(this);
805 theApp->clientlist->AddBannedClient( GetIP() );
807 AddDebugLogLineM( false, logClient, wxT("Client '") + GetUserName() + wxT("' seems to be an aggressive client and is banned from the uploadqueue"));
809 SetUploadState(US_BANNED);
811 Notify_ShowQueueCount(theStats::GetWaitingUserCount());
812 Notify_QlistRefreshClient(this);
815 bool CUpDownClient::IsBanned() const
817 return ( (theApp->clientlist->IsBannedClient(GetIP()) ) && m_nDownloadState != DS_DOWNLOADING);
820 void CUpDownClient::CheckForAggressive()
822 uint32 cur_time = ::GetTickCount();
824 // First call, initalize
825 if ( !m_LastFileRequest ) {
826 m_LastFileRequest = cur_time;
827 return;
830 // Is this an aggressive request?
831 if ( ( cur_time - m_LastFileRequest ) < MIN_REQUESTTIME ) {
832 m_Aggressiveness += 3;
834 // Is the client EVIL?
835 if ( m_Aggressiveness >= 10 && (!IsBanned() && m_nDownloadState != DS_DOWNLOADING )) {
836 AddDebugLogLineM( false, logClient, CFormat( wxT("Aggressive client banned (score: %d): %s -- %s -- %s") )
837 % m_Aggressiveness
838 % m_Username
839 % m_strModVersion
840 % m_fullClientVerString );
841 Ban();
843 } else {
844 // Polite request, reward client
845 if ( m_Aggressiveness )
846 m_Aggressiveness--;
849 m_LastFileRequest = cur_time;
853 void CUpDownClient::SetUploadFileID(const CMD4Hash& new_id)
855 // Update the uploading file found
856 CKnownFile* uploadingfile = theApp->sharedfiles->GetFileByID(new_id);
857 if ( !uploadingfile ) {
858 // Can this really happen?
859 uploadingfile = theApp->downloadqueue->GetFileByID(new_id);
861 SetUploadFileID(uploadingfile); // This will update queue count on old and new file.
864 void CUpDownClient::ProcessRequestPartsPacket(const byte* pachPacket, uint32 nSize, bool largeblocks) {
866 CMemFile data(pachPacket, nSize);
868 CMD4Hash reqfilehash = data.ReadHash();
870 uint64 auStartOffsets[3];
871 uint64 auEndOffsets[3];
873 if (largeblocks) {
874 auStartOffsets[0] = data.ReadUInt64();
875 auStartOffsets[1] = data.ReadUInt64();
876 auStartOffsets[2] = data.ReadUInt64();
878 auEndOffsets[0] = data.ReadUInt64();
879 auEndOffsets[1] = data.ReadUInt64();
880 auEndOffsets[2] = data.ReadUInt64();
881 } else {
882 auStartOffsets[0] = data.ReadUInt32();
883 auStartOffsets[1] = data.ReadUInt32();
884 auStartOffsets[2] = data.ReadUInt32();
886 auEndOffsets[0] = data.ReadUInt32();
887 auEndOffsets[1] = data.ReadUInt32();
888 auEndOffsets[2] = data.ReadUInt32();
891 for (unsigned int i = 0; i < itemsof(auStartOffsets); i++) {
892 AddDebugLogLineM(false, logClient,
893 wxString::Format(wxT("Client requests %u"), i)
894 + wxT(" ") + wxString::Format(wxT("File block %u-%u (%d bytes):"), auStartOffsets[i], auEndOffsets[i], auEndOffsets[i] - auStartOffsets[i])
895 + wxT(" ") + GetFullIP());
896 if (auEndOffsets[i] > auStartOffsets[i]) {
897 Requested_Block_Struct* reqblock = new Requested_Block_Struct;
898 reqblock->StartOffset = auStartOffsets[i];
899 reqblock->EndOffset = auEndOffsets[i];
900 md4cpy(reqblock->FileID, reqfilehash.GetHash());
901 reqblock->transferred = 0;
902 AddReqBlock(reqblock);
903 } else {
904 if (auEndOffsets[i] != 0 || auStartOffsets[i] != 0) {
905 AddDebugLogLineM(false, logClient, wxT("Client request is invalid!"));
911 void CUpDownClient::ProcessRequestPartsPacketv2(const CMemFile& data) {
913 CMD4Hash reqfilehash = data.ReadHash();
915 uint8 numblocks = data.ReadUInt8();
917 for (int i = 0; i < numblocks; i++) {
918 Requested_Block_Struct* reqblock = new Requested_Block_Struct;
919 try {
920 reqblock->StartOffset = data.GetIntTagValue();
921 // We have to do +1, because the block matching uses that.
922 reqblock->EndOffset = data.GetIntTagValue() + 1;
923 if ((reqblock->StartOffset || reqblock->EndOffset) && (reqblock->StartOffset > reqblock->EndOffset)) {
924 AddDebugLogLineM(false, logClient, wxString::Format(wxT("Client request is invalid! %i / %i"),reqblock->StartOffset, reqblock->EndOffset));
925 throw wxString(wxT("Client request is invalid!"));
928 AddDebugLogLineM(false, logClient,
929 wxString::Format(wxT("Client requests %u"), i)
930 + wxT(" ") + wxString::Format(wxT("File block %u-%u (%d bytes):"),reqblock->StartOffset, reqblock->EndOffset, reqblock->EndOffset - reqblock->StartOffset)
931 += wxT(" ") + GetFullIP());
933 md4cpy(reqblock->FileID, reqfilehash.GetHash());
934 reqblock->transferred = 0;
935 AddReqBlock(reqblock);
936 } catch (...) {
937 delete reqblock;
938 throw;
942 // File_checked_for_headers