1// 2// This file is part of the aMule Project. 3// 4// Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org ) 5// Copyright (c) 2002-2011 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. 10// 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. 15// 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 24// 25 26#include "updownclient.h" // Needed for CUpDownClient 27 28#include <protocol/Protocols.h> 29#include <protocol/ed2k/Client2Client/TCP.h> 30 31#include <zlib.h> 32 33#include "ClientCredits.h" // Needed for CClientCredits 34#include "Packet.h" // Needed for CPacket 35#include "MemFile.h" // Needed for CMemFile 36#include "UploadQueue.h" // Needed for CUploadQueue 37#include "DownloadQueue.h" // Needed for CDownloadQueue 38#include "PartFile.h" // Needed for PR_POWERSHARE 39#include "ClientTCPSocket.h" // Needed for CClientTCPSocket 40#include "SharedFileList.h" // Needed for CSharedFileList 41#include "amule.h" // Needed for theApp 42#include "ClientList.h" 43#include "Statistics.h" // Needed for theStats 44#include "Logger.h" 45#include "ScopedPtr.h" // Needed for CScopedArray 46#include "GuiEvents.h" // Needed for Notify_* 47#include "FileArea.h" // Needed for CFileArea 48 49 50// members of CUpDownClient 51// which are mainly used for uploading functions 52 53void CUpDownClient::SetUploadState(uint8 eNewState) 54{ 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(); 61 } 62 if (eNewState == US_UPLOADING) { 63 m_fSentOutOfPartReqs = 0; 64 } 65 66 // don't add any final cleanups for US_NONE here 67 m_nUploadState = eNewState; 68 UpdateDisplayedInfo(true); 69 } 70} 71 72uint32 CUpDownClient::CalculateScoreInternal() 73{ 74 //TODO: complete this (friends, uploadspeed, amuleuser etc etc) 75 if (m_Username.IsEmpty()) { 76 return 0; 77 } 78 79 if (credits == 0) { 80 return 0; 81 } 82 83 const CKnownFile* pFile = GetUploadFile(); 84 if ( !pFile ) { 85 return 0; 86 } 87 88 // bad clients (see note in function) 89 if (IsBadGuy()) { 90 return 0; 91 } 92 93 // friend slot 94 if (IsFriend() && GetFriendSlot() && !HasLowID()) { 95 return 0x0FFFFFFF; 96 } 97 98 if (IsBanned()) 99 return 0; 100 101 // score applies only to waiting clients, not to downloading clients 102 if (IsDownloading()) { 103 return 0; 104 } 105 106 // calculate score, based on waitingtime and other factors 107 float fBaseValue = (float)(::GetTickCount()-GetWaitStartTime())/1000; 108 109 fBaseValue *= GetScoreRatio(); // credits 110 111 // Take file upload priority into account 112 // 113 // One yet unsolved problem here: 114 // sometimes a client asks for 2 files and there is no way to decide, which file the 115 // client finally gets. so it could happen that he is queued first because of a 116 // high prio file, but then asks for something completely different. 117 float filepriority; 118 switch (pFile->GetUpPriority()) { 119 case PR_POWERSHARE: 120 filepriority = 250.0f; 121 break; 122 case PR_VERYHIGH: 123 filepriority = 1.8f; 124 break; 125 case PR_HIGH: 126 filepriority = 0.9f; 127 break; 128 case PR_LOW: 129 filepriority = 0.6f; 130 break; 131 case PR_VERYLOW: 132 filepriority = 0.2f; 133 break; 134 case PR_NORMAL: 135 default: 136 filepriority = 0.7f; 137 break; 138 } 139 fBaseValue *= filepriority; 140 141 if ( (IsEmuleClient() || GetClientSoft() < 10) && m_byEmuleVersion <= 0x19) { 142 fBaseValue *= 0.5f; 143 } 144 return (uint32)fBaseValue; 145} 146 147 148// Checks if it is next requested block from another chunk of the actual file or from another file 149// 150// [Returns] 151// true : Next requested block is from another different chunk or file than last downloaded block 152// false: Next requested block is from same chunk that last downloaded block 153bool CUpDownClient::IsDifferentPartBlock() const // [Tarod 12/22/2002] 154{ 155 bool different_part = false; 156 157 // Check if we have good lists and proceed to check for different chunks 158 if ((!m_BlockRequests_queue.empty()) && !m_DoneBlocks_list.empty()) 159 { 160 Requested_Block_Struct* last_done_block = NULL; 161 Requested_Block_Struct* next_requested_block = NULL; 162 uint64 last_done_part = 0xffffffff; 163 uint64 next_requested_part = 0xffffffff; 164 165 166 // Get last block and next pending 167 last_done_block = m_DoneBlocks_list.front(); 168 next_requested_block = m_BlockRequests_queue.front(); 169 170 // Calculate corresponding parts to blocks 171 last_done_part = last_done_block->StartOffset / PARTSIZE; 172 next_requested_part = next_requested_block->StartOffset / PARTSIZE; 173 174 // Test is we are asking same file and same part 175 if ( last_done_part != next_requested_part) { 176 different_part = true; 177 AddDebugLogLineN(logClient, wxT("Session ended due to new chunk.")); 178 } 179 180 if (md4cmp(last_done_block->FileID, next_requested_block->FileID) != 0) { 181 different_part = true; 182 AddDebugLogLineN(logClient, wxT("Session ended due to different file.")); 183 } 184 } 185 186 return different_part; 187} 188 189 190void CUpDownClient::CreateNextBlockPackage() 191{ 192 try { 193 // Buffer new data if current buffer is less than 100 KBytes 194 while (!m_BlockRequests_queue.empty() 195 && m_addedPayloadQueueSession - m_nCurQueueSessionPayloadUp < 100*1024) { 196 197 Requested_Block_Struct* currentblock = m_BlockRequests_queue.front(); 198 CKnownFile* srcfile = theApp->sharedfiles->GetFileByID(CMD4Hash(currentblock->FileID)); 199 200 if (!srcfile) { 201 throw wxString(wxT("requested file not found")); 202 } 203 204 // Check if this know file is a CPartFile. 205 // For completed part files IsPartFile() returns false, so they are 206 // correctly treated as plain CKnownFile. 207 CPartFile* srcPartFile = srcfile->IsPartFile() ? (CPartFile*)srcfile : NULL; 208 209 // THIS EndOffset points BEHIND the last byte requested 210 // (other than the offsets used in the PartFile code) 211 if (currentblock->EndOffset > srcfile->GetFileSize()) { 212 throw wxString(CFormat(wxT("Asked for data up to %d beyond end of file (%d)")) 213 % currentblock->EndOffset % srcfile->GetFileSize()); 214 } else if (currentblock->StartOffset > currentblock->EndOffset) { 215 throw wxString(CFormat(wxT("Asked for invalid block (start %d > end %d)")) 216 % currentblock->StartOffset % currentblock->EndOffset); 217 } 218 219 uint64 togo = currentblock->EndOffset - currentblock->StartOffset; 220 221 if (togo > EMBLOCKSIZE * 3) { 222 throw wxString(CFormat(wxT("Client requested too large block (%d > %d)")) 223 % togo % (EMBLOCKSIZE * 3)); 224 } 225 226 CFileArea area; 227 if (srcPartFile) { 228 if (!srcPartFile->IsComplete(currentblock->StartOffset,currentblock->EndOffset-1)) { 229 throw wxString(CFormat(wxT("Asked for incomplete block (%d - %d)")) 230 % currentblock->StartOffset % (currentblock->EndOffset-1)); 231 } 232 if (!srcPartFile->ReadData(area, currentblock->StartOffset, togo)) { 233 throw wxString(wxT("Failed to read from requested partfile")); 234 } 235 } else { 236 CFileAutoClose file; 237 CPath fullname = srcfile->GetFilePath().JoinPaths(srcfile->GetFileName()); 238 if ( !file.Open(fullname, CFile::read) ) { 239 // The file was most likely moved/deleted. So remove it from the list of shared files. 240 AddLogLineN(CFormat( _("Failed to open file (%s), removing from list of shared files.") ) % srcfile->GetFileName() ); 241 theApp->sharedfiles->RemoveFile(srcfile); 242 243 throw wxString(wxT("Failed to open requested file: Removing from list of shared files!")); 244 } 245 area.ReadAt(file, currentblock->StartOffset, togo); 246 } 247 area.CheckError(); 248 249 SetUploadFileID(srcfile); 250 251 // check extention to decide whether to compress or not 252 if (m_byDataCompVer == 1 && GetFiletype(srcfile->GetFileName()) != ftArchive) { 253 CreatePackedPackets(area.GetBuffer(), togo, currentblock); 254 } else { 255 CreateStandardPackets(area.GetBuffer(), togo, currentblock); 256 } 257 258 // file statistic 259 srcfile->statistic.AddTransferred(togo); 260 261 m_addedPayloadQueueSession += togo; 262 263 Requested_Block_Struct* block = m_BlockRequests_queue.front(); 264 265 m_BlockRequests_queue.pop_front(); 266 m_DoneBlocks_list.push_front(block); 267 } 268 269 return; 270 } catch (const wxString& DEBUG_ONLY(error)) { 271 AddDebugLogLineN(logClient, 272 CFormat(wxT("Client '%s' (%s) caused error while creating packet (%s) - disconnecting client")) 273 % GetUserName() % GetFullIP() % error); 274 } catch (const CIOFailureException& error) { 275 AddDebugLogLineC(logClient, wxT("IO failure while reading requested file: ") + error.what()); 276 } catch (const CEOFException& WXUNUSED(error)) { 277 AddDebugLogLineC(logClient, GetClientFullInfo() + wxT(" requested file-data at an invalid position - disconnecting")); 278 } 279 280 // Error occured. 281 theApp->uploadqueue->RemoveFromUploadQueue(this); 282} 283 284 285void CUpDownClient::CreateStandardPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock) 286{ 287 uint32 nPacketSize; 288 289 CMemFile memfile(buffer, togo); 290 if (togo > 10240) { 291 nPacketSize = togo/(uint32)(togo/10240); 292 } else { 293 nPacketSize = togo; 294 } 295 296 while (togo){ 297 if (togo < nPacketSize*2) { 298 nPacketSize = togo; 299 } 300 301 wxASSERT(nPacketSize); 302 togo -= nPacketSize; 303 304 uint64 endpos = (currentblock->EndOffset - togo); 305 uint64 startpos = endpos - nPacketSize; 306 307 bool bLargeBlocks = (startpos > 0xFFFFFFFF) || (endpos > 0xFFFFFFFF); 308 309 CMemFile data(nPacketSize + 16 + 2 * (bLargeBlocks ? 8 :4)); 310 data.WriteHash(GetUploadFileID()); 311 if (bLargeBlocks) { 312 data.WriteUInt64(startpos); 313 data.WriteUInt64(endpos); 314 } else { 315 data.WriteUInt32(startpos); 316 data.WriteUInt32(endpos); 317 } 318 char *tempbuf = new char[nPacketSize]; 319 memfile.Read(tempbuf, nPacketSize); 320 data.Write(tempbuf, nPacketSize); 321 delete [] tempbuf; 322 CPacket* packet = new CPacket(data, (bLargeBlocks ? OP_EMULEPROT : OP_EDONKEYPROT), (bLargeBlocks ? (uint8)OP_SENDINGPART_I64 : (uint8)OP_SENDINGPART)); 323 theStats::AddUpOverheadFileRequest(16 + 2 * (bLargeBlocks ? 8 :4)); 324 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize); 325 AddDebugLogLineN(logLocalClient, 326 CFormat(wxT("Local Client: %s to %s")) 327 % (bLargeBlocks ? wxT("OP_SENDINGPART_I64") : wxT("OP_SENDINGPART")) % GetFullIP() ); 328 m_socket->SendPacket(packet,true,false, nPacketSize); 329 } 330} 331 332 333void CUpDownClient::CreatePackedPackets(const byte* buffer, uint32 togo, Requested_Block_Struct* currentblock) 334{ 335 uLongf newsize = togo+300; 336 CScopedArray<byte> output(newsize); 337 uint16 result = compress2(output.get(), &newsize, buffer, togo, 9); 338 if (result != Z_OK || togo <= newsize){ 339 CreateStandardPackets(buffer, togo, currentblock); 340 return; 341 } 342 343 CMemFile memfile(output.get(), newsize); 344 345 uint32 totalPayloadSize = 0; 346 uint32 oldSize = togo; 347 togo = newsize; 348 uint32 nPacketSize; 349 if (togo > 10240) { 350 nPacketSize = togo/(uint32)(togo/10240); 351 } else { 352 nPacketSize = togo; 353 } 354 355 while (togo) { 356 if (togo < nPacketSize*2) { 357 nPacketSize = togo; 358 } 359 togo -= nPacketSize; 360 361 bool isLargeBlock = (currentblock->StartOffset > 0xFFFFFFFF) || (currentblock->EndOffset > 0xFFFFFFFF); 362 363 CMemFile data(nPacketSize + 16 + (isLargeBlock ? 12 : 8)); 364 data.WriteHash(GetUploadFileID()); 365 if (isLargeBlock) { 366 data.WriteUInt64(currentblock->StartOffset); 367 } else { 368 data.WriteUInt32(currentblock->StartOffset); 369 } 370 data.WriteUInt32(newsize); 371 char *tempbuf = new char[nPacketSize]; 372 memfile.Read(tempbuf, nPacketSize); 373 data.Write(tempbuf,nPacketSize); 374 delete [] tempbuf; 375 CPacket* packet = new CPacket(data, OP_EMULEPROT, (isLargeBlock ? OP_COMPRESSEDPART_I64 : OP_COMPRESSEDPART)); 376 377 // approximate payload size 378 uint32 payloadSize = nPacketSize*oldSize/newsize; 379 380 if (togo == 0 && totalPayloadSize+payloadSize < oldSize) { 381 payloadSize = oldSize-totalPayloadSize; 382 } 383 384 totalPayloadSize += payloadSize; 385 386 // put packet directly on socket 387 theStats::AddUpOverheadFileRequest(24); 388 theStats::AddUploadToSoft(GetClientSoft(), nPacketSize); 389 AddDebugLogLineN(logLocalClient, 390 CFormat(wxT("Local Client: %s to %s")) 391 % (isLargeBlock ? wxT("OP_COMPRESSEDPART_I64") : wxT("OP_COMPRESSEDPART")) % GetFullIP() ); 392 m_socket->SendPacket(packet,true,false, payloadSize); 393 } 394} 395 396 397void CUpDownClient::ProcessExtendedInfo(const CMemFile *data, CKnownFile *tempreqfile) 398{ 399 m_uploadingfile->UpdateUpPartsFrequency( this, false ); // Decrement 400 m_upPartStatus.clear(); 401 m_nUpCompleteSourcesCount= 0; 402 403 if( GetExtendedRequestsVersion() == 0 ) { 404 // Something is coded wrong on this client if he's sending something it doesn't advertise. 405 return; 406 } 407 408 if (data->GetLength() == 16) { 409 // Wrong again. Advertised >0 but send a 0-type packet. 410 // But this time we'll disconnect it. 411 throw CInvalidPacket(wxT("Wrong size on extended info packet")); 412 } 413 414 uint16 nED2KUpPartCount = data->ReadUInt16(); 415 if (!nED2KUpPartCount) { 416 m_upPartStatus.setsize( tempreqfile->GetPartCount(), 0 ); 417 } else { 418 if (tempreqfile->GetED2KPartCount() != nED2KUpPartCount) { 419 // We already checked if we are talking about the same file.. So if we get here, something really strange happened! 420 m_upPartStatus.clear(); 421 return; 422 } 423 424 m_upPartStatus.setsize( tempreqfile->GetPartCount(), 0 ); 425 426 try { 427 uint16 done = 0; 428 while (done != m_upPartStatus.size()) { 429 uint8 toread = data->ReadUInt8(); 430 for (sint32 i = 0;i != 8;i++){ 431 m_upPartStatus.set(done, (toread>>i)&1); 432 // We may want to use this for another feature.. 433 // if (m_upPartStatus[done] && !tempreqfile->IsComplete(done*PARTSIZE,((done+1)*PARTSIZE)-1)) 434 // bPartsNeeded = true; 435 done++; 436 if (done == m_upPartStatus.size()) { 437 break; 438 } 439 } 440 } 441 } catch (...) { 442 // We want the increment the frequency even if we didn't read everything 443 m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment 444 445 throw; 446 } 447 448 if (GetExtendedRequestsVersion() > 1) { 449 uint16 nCompleteCountLast = GetUpCompleteSourcesCount(); 450 uint16 nCompleteCountNew = data->ReadUInt16(); 451 SetUpCompleteSourcesCount(nCompleteCountNew); 452 if (nCompleteCountLast != nCompleteCountNew) { 453 tempreqfile->UpdatePartsInfo(); 454 } 455 } 456 } 457 458 m_uploadingfile->UpdateUpPartsFrequency( this, true ); // Increment 459 460 Notify_SharedCtrlRefreshClient(ECID(), AVAILABLE_SOURCE); 461} 462 463 464void CUpDownClient::SetUploadFileID(CKnownFile* newreqfile) 465{ 466 if (m_uploadingfile == newreqfile) { 467 return; 468 } else if (m_uploadingfile) { 469 m_uploadingfile->RemoveUploadingClient(this); 470 m_uploadingfile->UpdateUpPartsFrequency(this, false); // Decrement 471 } 472 473 if (newreqfile) { 474 // This is a new file! update info 475 newreqfile->AddUploadingClient(this); 476 477 if (m_requpfileid != newreqfile->GetFileHash()) { 478 m_requpfileid = newreqfile->GetFileHash(); 479 m_upPartStatus.setsize( newreqfile->GetPartCount(), 0 ); 480 } else { 481 // this is the same file we already had assigned. Only update data. 482 newreqfile->UpdateUpPartsFrequency(this, true); // Increment 483 } 484 485 m_uploadingfile = newreqfile; 486 } else { 487 m_upPartStatus.clear(); 488 m_nUpCompleteSourcesCount = 0; 489 // This clears m_uploadingfile and m_requpfileid 490 ClearUploadFileID(); 491 } 492} 493 494 495void CUpDownClient::AddReqBlock(Requested_Block_Struct* reqblock) 496{ 497 if (GetUploadState() != US_UPLOADING) { 498 AddDebugLogLineN(logRemoteClient, wxT("UploadClient: Client tried to add requested block when not in upload slot! Prevented requested blocks from being added.")); 499 delete reqblock; 500 return; 501 } 502 503 { 504 std::list<Requested_Block_Struct*>::iterator it = m_DoneBlocks_list.begin(); 505 for (; it != m_DoneBlocks_list.end(); ++it) { 506 if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) { 507 delete reqblock; 508 return; 509 } 510 } 511 } 512 513 { 514 std::list<Requested_Block_Struct*>::iterator it = m_BlockRequests_queue.begin(); 515 for (; it != m_BlockRequests_queue.end(); ++it) { 516 if (reqblock->StartOffset == (*it)->StartOffset && reqblock->EndOffset == (*it)->EndOffset) { 517 delete reqblock; 518 return; 519 } 520 } 521 } 522 523 m_BlockRequests_queue.push_back(reqblock); 524} 525 526 527uint32 CUpDownClient::GetWaitStartTime() const 528{ 529 uint32 dwResult = 0; 530 531 if ( credits ) { 532 dwResult = credits->GetSecureWaitStartTime(GetIP()); 533 534 if (dwResult > m_dwUploadTime && IsDownloading()) { 535 // This happens only if two clients with invalid securehash are in the queue - if at all 536 dwResult = m_dwUploadTime - 1; 537 } 538 } 539 540 return dwResult; 541} 542 543 544void CUpDownClient::SetWaitStartTime() 545{ 546 if ( credits ) { 547 credits->SetSecWaitStartTime(GetIP()); 548 } 549} 550 551 552void CUpDownClient::ClearWaitStartTime() 553{ 554 if ( credits ) { 555 credits->ClearWaitStartTime(); 556 } 557} 558 559 560void CUpDownClient::ResetSessionUp() 561{ 562 m_nCurSessionUp = m_nTransferredUp; 563 m_addedPayloadQueueSession = 0; 564 m_nCurQueueSessionPayloadUp = 0; 565 // If upload was resumed there can be a remaining payload in the socket 566 // causing (prepared - sent) getting negative. So reset the counter here. 567 if (m_socket) { 568 CEMSocket* s = m_socket; 569 s->GetSentPayloadSinceLastCallAndReset(); 570 } 571} 572 573 574uint32 CUpDownClient::SendBlockData() 575{ 576 uint32 curTick = ::GetTickCount(); 577 uint64 sentBytesCompleteFile = 0; 578 uint64 sentBytesPartFile = 0; 579 uint64 sentBytesPayload = 0; 580 581 if (m_socket) { 582 CEMSocket* s = m_socket; 583// uint32 uUpStatsPort = GetUserPort(); 584 585 // Extended statistics information based on which client software and which port we sent this data to... 586 // This also updates the grand total for sent bytes, etc. And where this data came from. 587 sentBytesCompleteFile = s->GetSentBytesCompleteFileSinceLastCallAndReset(); 588 sentBytesPartFile = s->GetSentBytesPartFileSinceLastCallAndReset(); 589// thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, false, true, sentBytesCompleteFile, (IsFriend() && GetFriendSlot())); 590// thePrefs.Add2SessionTransferData(GetClientSoft(), uUpStatsPort, true, true, sentBytesPartFile, (IsFriend() && GetFriendSlot())); 591 592 m_nTransferredUp += sentBytesCompleteFile + sentBytesPartFile; 593 credits->AddUploaded(sentBytesCompleteFile + sentBytesPartFile, GetIP(), theApp->CryptoAvailable()); 594 595 sentBytesPayload = s->GetSentPayloadSinceLastCallAndReset(); 596 m_nCurQueueSessionPayloadUp += sentBytesPayload; 597 598 if (theApp->uploadqueue->CheckForTimeOver(this)) { 599 theApp->uploadqueue->RemoveFromUploadQueue(this); 600 SendOutOfPartReqsAndAddToWaitingQueue(); 601 } else { 602 // read blocks from file and put on socket 603 CreateNextBlockPackage(); 604 } 605 } 606 607 if(sentBytesCompleteFile + sentBytesPartFile > 0 || 608 m_AvarageUDR_list.empty() || (curTick - m_AvarageUDR_list.back().timestamp) > 1*1000) { 609 // Store how much data we've transferred this round, 610 // to be able to calculate average speed later 611 // keep sum of all values in list up to date 612 TransferredData newitem = {sentBytesCompleteFile + sentBytesPartFile, curTick}; 613 m_AvarageUDR_list.push_back(newitem); 614 m_nSumForAvgUpDataRate += sentBytesCompleteFile + sentBytesPartFile; 615 } 616 617 // remove to old values in list 618 while ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 10*1000) { 619 // keep sum of all values in list up to date 620 m_nSumForAvgUpDataRate -= m_AvarageUDR_list.front().datalen; 621 m_AvarageUDR_list.pop_front(); 622 } 623 624 // Calculate average speed for this slot 625 if ((!m_AvarageUDR_list.empty()) && (curTick - m_AvarageUDR_list.front().timestamp) > 0 && GetUpStartTimeDelay() > 2*1000) { 626 m_nUpDatarate = ((uint64)m_nSumForAvgUpDataRate*1000) / (curTick-m_AvarageUDR_list.front().timestamp); 627 } else { 628 // not enough values to calculate trustworthy speed. Use -1 to tell this 629 m_nUpDatarate = 0; //-1; 630 } 631 632 // Check if it's time to update the display. 633 m_cSendblock++; 634 if (m_cSendblock == 30){ 635 m_cSendblock = 0; 636 Notify_SharedCtrlRefreshClient(ECID(), AVAILABLE_SOURCE); 637 } 638 639 return sentBytesCompleteFile + sentBytesPartFile; 640} 641 642 643void CUpDownClient::SendOutOfPartReqsAndAddToWaitingQueue() 644{ 645 // Kry - this is actually taken from eMule, but makes a lot of sense ;) 646 647 //OP_OUTOFPARTREQS will tell the downloading client to go back to OnQueue.. 648 //The main reason for this is that if we put the client back on queue and it goes 649 //back to the upload before the socket times out... We get a situation where the 650 //downloader thinks it already sent the requested blocks and the uploader thinks 651 //the downloader didn't send any request blocks. Then the connection times out.. 652 //I did some tests with eDonkey also and it seems to work well with them also.. 653 654 // Send this inmediately, don't queue. 655 CPacket* pPacket = new CPacket(OP_OUTOFPARTREQS, 0, OP_EDONKEYPROT); 656 theStats::AddUpOverheadFileRequest(pPacket->GetPacketSize()); 657 AddDebugLogLineN( logLocalClient, wxT("Local Client: OP_OUTOFPARTREQS to ") + GetFullIP() ); 658 SendPacket(pPacket, true, true); 659 660 theApp->uploadqueue->AddClientToQueue(this); 661} 662 663 664/** 665 * See description for CEMSocket::TruncateQueues(). 666 */ 667void CUpDownClient::FlushSendBlocks() 668{ 669 // Call this when you stop upload, or the socket might be not able to send 670 if (m_socket) { //socket may be NULL... 671 m_socket->TruncateQueues(); 672 } 673} 674 675 676void CUpDownClient::SendHashsetPacket(const CMD4Hash& forfileid) 677{ 678 CKnownFile* file = theApp->sharedfiles->GetFileByID( forfileid ); 679 bool from_dq = false; 680 if ( !file ) { 681 from_dq = true; 682 if ((file = theApp->downloadqueue->GetFileByID(forfileid)) == NULL) { 683 AddLogLineN(CFormat( _("Hashset requested for unknown file: %s") ) % forfileid.Encode() ); 684 685 return; 686 } 687 } 688 689 if ( !file->GetHashCount() ) { 690 if (from_dq) { 691 AddDebugLogLineN(logRemoteClient, wxT("Requested hashset could not be found")); 692 return; 693 } else { 694 file = theApp->downloadqueue->GetFileByID(forfileid); 695 if (!(file && file->GetHashCount())) { 696 AddDebugLogLineN(logRemoteClient, wxT("Requested hashset could not be found")); 697 return; 698 } 699 } 700 } 701 702 CMemFile data(1024); 703 data.WriteHash(file->GetFileHash()); 704 uint16 parts = file->GetHashCount(); 705 data.WriteUInt16(parts); 706 for (int i = 0; i != parts; i++) { 707 data.WriteHash(file->GetPartHash(i)); 708 } 709 CPacket* packet = new CPacket(data, OP_EDONKEYPROT, OP_HASHSETANSWER); 710 theStats::AddUpOverheadFileRequest(packet->GetPacketSize()); 711 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_HASHSETANSWER to ") + GetFullIP()); 712 SendPacket(packet,true,true); 713} 714 715 716void CUpDownClient::ClearUploadBlockRequests() 717{ 718 FlushSendBlocks(); 719 DeleteContents(m_BlockRequests_queue); 720 DeleteContents(m_DoneBlocks_list); 721} 722 723void CUpDownClient::SendRankingInfo(){ 724 if (!ExtProtocolAvailable()) { 725 return; 726 } 727 728 uint16 nRank = GetUploadQueueWaitingPosition(); 729 if (!nRank) { 730 return; 731 } 732 733 CMemFile data; 734 data.WriteUInt16(nRank); 735 // Kry: what are these zero bytes for. are they really correct? 736 // Kry - Well, eMule does like that. I guess they're ok. 737 data.WriteUInt32(0); data.WriteUInt32(0); data.WriteUInt16(0); 738 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_QUEUERANKING); 739 theStats::AddUpOverheadOther(packet->GetPacketSize()); 740 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_QUEUERANKING to ") + GetFullIP()); 741 SendPacket(packet,true,true); 742} 743 744 745void CUpDownClient::SendCommentInfo(CKnownFile* file) 746{ 747 if (!m_bCommentDirty || file == NULL || !ExtProtocolAvailable() || m_byAcceptCommentVer < 1) { 748 return; 749 } 750 m_bCommentDirty = false; 751 752 // Truncate to max len. 753 wxString desc = file->GetFileComment().Left(MAXFILECOMMENTLEN); 754 uint8 rating = file->GetFileRating(); 755 756 if ( file->GetFileRating() == 0 && desc.IsEmpty() ) { 757 return; 758 } 759 760 CMemFile data(256); 761 data.WriteUInt8(rating); 762 data.WriteString(desc, GetUnicodeSupport(), 4 /* size it's uint32 */); 763 764 CPacket* packet = new CPacket(data, OP_EMULEPROT, OP_FILEDESC); 765 theStats::AddUpOverheadOther(packet->GetPacketSize()); 766 AddDebugLogLineN(logLocalClient, wxT("Local Client: OP_FILEDESC to ") + GetFullIP()); 767 SendPacket(packet,true); 768} 769 770void CUpDownClient::UnBan(){ 771 m_Aggressiveness = 0; 772 773 theApp->clientlist->AddTrackClient(this); 774 theApp->clientlist->RemoveBannedClient( GetIP() ); 775 SetUploadState(US_NONE); 776 ClearWaitStartTime(); 777} 778 779void CUpDownClient::Ban(){ 780 theApp->clientlist->AddTrackClient(this); 781 theApp->clientlist->AddBannedClient( GetIP() ); 782 783 AddDebugLogLineN(logClient, wxT("Client '") + GetUserName() + wxT("' seems to be an aggressive client and is banned from the uploadqueue")); 784 785 SetUploadState(US_BANNED); 786 787 Notify_SharedCtrlRefreshClient(ECID(), UNAVAILABLE_SOURCE); 788} 789 790bool CUpDownClient::IsBanned() const 791{ 792 return ( (theApp->clientlist->IsBannedClient(GetIP()) ) && m_nDownloadState != DS_DOWNLOADING); 793} 794 795void CUpDownClient::CheckForAggressive() 796{ 797 uint32 cur_time = ::GetTickCount(); 798 799 // First call, initalize 800 if ( !m_LastFileRequest ) { 801 m_LastFileRequest = cur_time; 802 return; 803 } 804 805 // Is this an aggressive request? 806 if ( ( cur_time - m_LastFileRequest ) < MIN_REQUESTTIME ) { 807 m_Aggressiveness += 3; 808 809 // Is the client EVIL? 810 if ( m_Aggressiveness >= 10 && (!IsBanned() && m_nDownloadState != DS_DOWNLOADING )) { 811 AddDebugLogLineN(logClient, CFormat( wxT("Aggressive client banned (score: %d): %s -- %s -- %s") ) 812 % m_Aggressiveness 813 % m_Username 814 % m_strModVersion 815 % m_fullClientVerString ); 816 Ban(); 817 } 818 } else { 819 // Polite request, reward client 820 if ( m_Aggressiveness ) 821 m_Aggressiveness--; 822 } 823 824 m_LastFileRequest = cur_time; 825} 826 827 828void CUpDownClient::SetUploadFileID(const CMD4Hash& new_id) 829{ 830 // Update the uploading file found 831 CKnownFile* uploadingfile = theApp->sharedfiles->GetFileByID(new_id); 832 if ( !uploadingfile ) { 833 // Can this really happen? 834 uploadingfile = theApp->downloadqueue->GetFileByID(new_id); 835 } 836 SetUploadFileID(uploadingfile); // This will update queue count on old and new file. 837} 838 839void CUpDownClient::ProcessRequestPartsPacket(const byte* pachPacket, uint32 nSize, bool largeblocks) { 840 841 CMemFile data(pachPacket, nSize); 842 843 CMD4Hash reqfilehash = data.ReadHash(); 844 845 uint64 auStartOffsets[3]; 846 uint64 auEndOffsets[3]; 847 848 if (largeblocks) { 849 auStartOffsets[0] = data.ReadUInt64(); 850 auStartOffsets[1] = data.ReadUInt64(); 851 auStartOffsets[2] = data.ReadUInt64(); 852 853 auEndOffsets[0] = data.ReadUInt64(); 854 auEndOffsets[1] = data.ReadUInt64(); 855 auEndOffsets[2] = data.ReadUInt64(); 856 } else { 857 auStartOffsets[0] = data.ReadUInt32(); 858 auStartOffsets[1] = data.ReadUInt32(); 859 auStartOffsets[2] = data.ReadUInt32(); 860 861 auEndOffsets[0] = data.ReadUInt32(); 862 auEndOffsets[1] = data.ReadUInt32(); 863 auEndOffsets[2] = data.ReadUInt32(); 864 } 865 866 for (unsigned int i = 0; i < itemsof(auStartOffsets); i++) { 867 AddDebugLogLineN(logClient, 868 CFormat(wxT("Client %s requests %d File block %d-%d (%d bytes):")) 869 % GetFullIP() % i % auStartOffsets[i] % auEndOffsets[i] 870 % (auEndOffsets[i] - auStartOffsets[i])); 871 if (auEndOffsets[i] > auStartOffsets[i]) { 872 Requested_Block_Struct* reqblock = new Requested_Block_Struct; 873 reqblock->StartOffset = auStartOffsets[i]; 874 reqblock->EndOffset = auEndOffsets[i]; 875 md4cpy(reqblock->FileID, reqfilehash.GetHash()); 876 reqblock->transferred = 0; 877 AddReqBlock(reqblock); 878 } else { 879 if (auEndOffsets[i] != 0 || auStartOffsets[i] != 0) { 880 AddDebugLogLineN(logClient, wxT("Client request is invalid!")); 881 } 882 } 883 } 884} 885 886void CUpDownClient::ProcessRequestPartsPacketv2(const CMemFile& data) { 887 888 CMD4Hash reqfilehash = data.ReadHash(); 889 890 uint8 numblocks = data.ReadUInt8(); 891 892 for (int i = 0; i < numblocks; i++) { 893 Requested_Block_Struct* reqblock = new Requested_Block_Struct; 894 try { 895 reqblock->StartOffset = data.GetIntTagValue(); 896 // We have to do +1, because the block matching uses that. 897 reqblock->EndOffset = data.GetIntTagValue() + 1; 898 if ((reqblock->StartOffset || reqblock->EndOffset) && (reqblock->StartOffset > reqblock->EndOffset)) { 899 AddDebugLogLineN(logClient, CFormat(wxT("Client %s request is invalid! %d / %d")) 900 % GetFullIP() % reqblock->StartOffset % reqblock->EndOffset); 901 throw wxString(wxT("Client request is invalid!")); 902 } 903 904 AddDebugLogLineN(logClient, 905 CFormat(wxT("Client %s requests %d File block %d-%d (%d bytes):")) 906 % GetFullIP() % i % reqblock->StartOffset % reqblock->EndOffset 907 % (reqblock->EndOffset - reqblock->StartOffset)); 908 909 md4cpy(reqblock->FileID, reqfilehash.GetHash()); 910 reqblock->transferred = 0; 911 AddReqBlock(reqblock); 912 } catch (...) { 913 delete reqblock; 914 throw; 915 } 916 } 917} 918// File_checked_for_headers 919